React 开发必须知道的 34 个技巧【近1W字】

React 是前端三大框架之一,在开发中也是一项技能;
本文从实际开发中总结了 React 开发的一些技巧技巧,适合 React 初学或者有一定项目经验的同学
序列文章:Vue 开发必须知道的 36 个技巧【近1W字】
源码地址
请戳, 欢迎 star
效果图

1 组件通讯
1.1 props
子组件
import React from “react”;
import PropTypes from “prop-types”;
import { Button } from “antd”;

export default class EightteenChildOne extends React.Component {
static propTypes = { //propTypes校验传入类型,详情在技巧11
name: PropTypes.string
};

click = () => {
// 通过触发方法子传父
this.props.eightteenChildOneToFather(“这是 props 改变父元素的值”);
};

render() {
return (


这是通过 props 传入的值{this.props.name}


点击改变父元素值


);
}
}

复制代码父组件
<EightteenChildOne name={‘props 传入的 name 值’} eightteenChildOneToFather={(mode)=>this.eightteenChildOneToFather(mode)}>
复制代码props 传多个值时:
传统写法
const {dataOne,dataTwo,dataThree} = this.state

复制代码升级写法
<Com {…{dataOne,dataTwo,dataThree}}>
复制代码1.2 props 升级版
原理:子组件里面利用 props 获取父组件方法直接调用,从而改变父组件的值
注意: 此方法和 props 大同小异,都是 props 的应用,所以在源码中没有举例
调用父组件方法改变该值
// 父组件
state = {
count: {}
}
changeParentState = obj => {
this.setState(obj);
}
// 子组件
onClick = () => {
this.props.changeParentState({ count: 2 });
}
复制代码1.3 Provider,Consumer和Context
1.Context在 16.x 之前是定义一个全局的对象,类似 vue 的 eventBus,如果组件要使用到该值直接通过this.context获取
//根组件
class MessageList extends React.Component {
getChildContext() {
return {color: “purple”,text: “item text”};
}

render() {
const children = this.props.messages.map((message) =>

);
return

{children}
;
}
}

MessageList.childContextTypes = {
color: React.PropTypes.string
text: React.PropTypes.string
};

//中间组件
class Message extends React.Component {
render() {
return (



Delete

);
}
}

//孙组件(接收组件)
class MessageItem extends React.Component {
render() {
return (


{this.context.text}

);
}
}

MessageItem.contextTypes = {
text: React.PropTypes.string //React.PropTypes在 15.5 版本被废弃,看项目实际的 React 版本
};

class Button extends React.Component {
render() {
return (
<button style={{background: this.context.color}}>
{this.props.children}

);
}
}

Button.contextTypes = {
color: React.PropTypes.string
};
复制代码2.16.x 之后的Context使用了Provider和Customer模式,在顶层的Provider中传入value,在子孙级的Consumer中获取该值,并且能够传递函数,用来修改context
声明一个全局的 context 定义,context.js
import React from ‘react’
let { Consumer, Provider } = React.createContext();//创建 context 并暴露Consumer和Provider模式
export {
Consumer,
Provider
}
复制代码父组件导入
// 导入 Provider
import {Provider} from “…/…/utils/context”

componentDidMount(){
this.props.onRef(this)
console.log(this) // ->将EightteenChildFour传递给父组件this.props.onRef()方法
}

click = () => {
this.setState({name:‘这是组件click 方法改变EightteenChildFour改变的name 值’})
};

render() {
return (


{this.state.name}


点击改变组件EightteenChildFour的name 值


);
}
}
复制代码eighteen.jsx

eightteenChildFourRef = (ref)=>{
console.log(‘eightteenChildFour的Ref值为’)
// 获取的 ref 里面包括整个组件实例
console.log(ref)
// 调用子组件方法
ref.click()
}
复制代码1.7 ref
原理:就是通过 React 的 ref 属性获取到整个子组件实例,再进行操作
EightteenChildFive.jsx
// 常用的组件定义方法
export default class EightteenChildFive extends React.Component {
state={
name:‘这是组件EightteenChildFive的name 值’
}

click = () => {
this.setState({name:‘这是组件click 方法改变EightteenChildFive改变的name 值’})
};

render() {
return (


{this.state.name}


点击改变组件EightteenChildFive的name 值


);
}
}
复制代码eighteen.jsx
// 钩子获取实例
componentDidMount(){
console.log(‘eightteenChildFive的Ref值为’)
// 获取的 ref 里面包括整个组件实例,同样可以拿到子组件的实例
console.log(this.refs[“eightteenChildFiveRef”])
}

// 组件定义 ref 属性

复制代码1.8 redux
redux 是一个独立的事件通讯插件,这里就不做过多的叙述
请戳传送门:
1.9 MobX
MobX 也是一个独立的事件通讯插件,这里就不做过多的叙述
请戳传送门:
1.10 flux
flux 也是一个独立的事件通讯插件,这里就不做过多的叙述
请戳传送门:
1.11 hooks
1.hooks 是利用 userReducer 和 context 实现通讯,下面模拟实现一个简单的 redux
2.核心文件分为 action,reducer,types
action.js
import * as Types from ‘./types’;

export const onChangeCount = count => ({
type: Types.EXAMPLE_TEST,
count: count + 1
})
复制代码reducer.js
import * as Types from “./types”;
export const defaultState = {
count: 0
};
export default (state, action) => {
switch (action.type) {
case Types.EXAMPLE_TEST:
return {
…state,
count: action.count
};
default: {
return state;
}
}
};
复制代码types.js
export const EXAMPLE_TEST = ‘EXAMPLE_TEST’;
复制代码eightteen.jsx
export const ExampleContext = React.createContext(null);//创建createContext上下文

// 定义组件
function ReducerCom() {
const [exampleState, exampleDispatch] = useReducer(example, defaultState);

return (
<ExampleContext.Provider
value={{ exampleState, dispatch: exampleDispatch }}
>

</ExampleContext.Provider>
);
}
复制代码EightteenChildThree.jsx // 组件
import React, { useEffect, useContext } from ‘react’;
import {Button} from ‘antd’

import {onChangeCount} from ‘…/…/pages/TwoTen/store/action’;
import { ExampleContext } from ‘…/…/pages/TwoTen/eighteen’;

const Example = () => {

const exampleContext = useContext(ExampleContext);

useEffect(() => { // 监听变化
    console.log('变化执行啦')
}, [exampleContext.exampleState.count]);

return (
    <div>
        <p>值为{exampleContext.exampleState.count}</p>
        <Button onClick={() => exampleContext.dispatch(onChangeCount(exampleContext.exampleState.count))}>点击加 1</Button>
    </div>
)

}

export default Example;
复制代码3.hooks其实就是对原有React 的 API 进行了封装,暴露比较方便使用的钩子;
4.钩子有:

钩子名
作用

useState
初始化和设置状态

useEffect
componentDidMount,componentDidUpdate和componentWillUnmount和结合体,所以可以监听useState定义值的变化

useContext
定义一个全局的对象,类似 context

useReducer
可以增强函数提供类似 Redux 的功能

useCallback
记忆作用,共有两个参数,第一个参数为一个匿名函数,就是我们想要创建的函数体。第二参数为一个数组,里面的每一项是用来判断是否需要重新创建函数体的变量,如果传入的变量值保持不变,返回记忆结果。如果任何一项改变,则返回新的结果

useMemo
作用和传入参数与 useCallback 一致,useCallback返回函数,useDemo 返回值

useRef
获取 ref 属性对应的 dom

useImperativeMethods
自定义使用ref时公开给父组件的实例值

useMutationEffect
作用与useEffect相同,但在更新兄弟组件之前,它在React执行其DOM改变的同一阶段同步触发

useLayoutEffect
作用与useEffect相同,但在所有DOM改变后同步触发

5.useImperativeMethods
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeMethods(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} … />;
}
FancyInput = forwardRef(FancyInput);
复制代码更多hooks 介绍请戳
1.12 对比

方法
优点
缺点

props
不需要引入外部插件
兄弟组件通讯需要建立共同父级组件,麻烦

props 升级版
不需要引入外部插件,子传父,不需要在父组件用方法接收
同 props

Provider,Consumer和Context
不需要引入外部插件,跨多级组件或者兄弟组件通讯利器
状态数据状态追踪麻烦

EventEmitter
可支持兄弟,父子组件通讯
要引入外部插件

路由传参
可支持兄弟组件传值,页面简单数据传递非常方便
父子组件通讯无能为力

onRef
可以在获取整个子组件实例,使用简单
兄弟组件通讯麻烦,官方不建议使用

ref
同 onRef
同 onRef

redux
建立了全局的状态管理器,兄弟父子通讯都可解决
引入了外部插件

mobx
建立了全局的状态管理器,兄弟父子通讯都可解决
引入了外部插件

flux
建立了全局的状态管理器,兄弟父子通讯都可解决
引入了外部插件

hooks
16.x 新的属性,可支持兄弟,父子组件通讯
需要结合 context 一起使用

redux , mobx和flux对比

方法
介绍

redux
1.核心模块:Action,Reducer,Store;2. Store 和更改逻辑是分开的;3. 只有一个 Store;4. 带有分层 reducer 的单一 Store;5. 没有调度器的概念;6. 容器组件是有联系的;7. 状态是不可改变的;8.更多的是遵循函数式编程思想

mobx
1.核心模块:Action,Reducer,Derivation;2.有多个 store;3.设计更多偏向于面向对象编程和响应式编程,通常将状态包装成可观察对象,一旦状态对象变更,就能自动获得更新

flux
1.核心模块:Store,ReduceStore,Container;2.有多个 store;

2.require.context()
这个是 webpack 的 api,这个在 vue 技巧中有介绍,因为 Vue 和 React 工程都是基于 webpack打包,所以在 react 也可以使用
const path = require(‘path’)
const files = require.context(’@/components/home’, false, /.vue$/)
const modules = {}
files.keys().forEach(key => {
const name = path.basename(key, ‘.vue’)
modules[name] = files(key).default || files(key)
})

复制代码3.Decorator
定义:decorator是ES7的一个新特性,可以修改class的属性
import React from ‘react’
import Test from ‘…/…/utils/decorators’

@Test
//只要Decorator后面是Class,默认就已经把Class当成参数隐形传进Decorator了。
class TwentyNine extends React.Component{
componentDidMount(){
console.log(this,‘decorator.js’) // 这里的this是类的一个实例
console.log(this.testable)
}
render(){
return (

这是技巧23

)
}
}

export default TwentyNine
复制代码decorators.js
function testable(target) {
console.log(target)
target.isTestable = true;
target.prototype.getDate = ()=>{
console.log( new Date() )
}
}

export default testable
复制代码很多中间件,像 redux 里面就封装了Decorator的使用
4.使用 if…else
场景:有些时候需要根据不同状态值页面显示不同内容
import React from “react”;

export default class Four extends React.Component {
state = {
count: 1
};
render() {
let info
if(this.state.count=0){
info=(
这是数量为 0 显示
)
} else if(this.state.count
=1){
info=(
这是数量为 1 显示
)
}
return (


{info}

);
}
}
复制代码5.state 值改变的四种方式
方式 1
let {count} = this.state
this.setState({count:2})
复制代码方式 2:callBack
this.setState(({count})=>({count:count+2}))
复制代码方式 3:接收 state 和 props 参数
this.setState((state, props) => {
return { count: state.count + props.step };
});
复制代码方式 4:hooks
const [count, setCount] = useState(0)
// 设置值
setCount(count+2)
复制代码6.监听states 变化
1.16.x 之前使用componentWillReveiveProps
componentWillReceiveProps (nextProps){
if(this.props.visible !== nextProps.visible){
//props 值改变做的事
}
}
复制代码注意:有些时候componentWillReceiveProps在 props 值未变化也会触发,因为在生命周期的第一次render后不会被调用,但是会在之后的每次render中被调用 = 当父组件再次传送props
2.16.x 之后使用getDerivedStateFromProps,16.x 以后componentWillReveiveProps也未移除
export default class Six extends React.Component {
state = {
countOne:1,
changeFlag:’’
};
clickOne(){
let {countOne} = this.state
this.setState({countOne:countOne+1})
};
static getDerivedStateFromProps (nextProps){
console.log(‘变化执行’)
return{
changeFlag:‘state 值变化执行’
}
}
render() {
const {countOne,changeFlag} = this.state
return (


点击加 1 countOne 值为{countOne}
{changeFlag}



);
}
}
复制代码7.组件定义方法
方式 1:ES5 的Function 定义
function FunCom(props){
return
这是Function 定义的组件

}
ReactDOM.render(, mountNode)

// 在 hooks 未出来之前,这个是定义无状态组件的方法,现在有了 hooks 也可以处理状态
复制代码方式 2: ES5的 createClass 定义
const CreateClassCom = React.createClass({
render: function() {
return

这是React.createClass定义的组件

}
});
复制代码方式 3:ES6 的 extends
class Com extends React.Component {
render(){
return(
这是React.Component定义的组件
)
}
}
复制代码调用
export default class Seven extends React.Component {
render() {
return (




);
}
}
复制代码区别: ES5的 createClass是利用function模拟class的写法做出来的es6;
通过es6新增class的属性创建的组件此组件创建简单.
8.通过 ref 属性获取 component
方式 1:也是最早的用法,通过 this.refs[属性名获取]
也可以作用到组件上,从而拿到组件实例
class RefOne extends React.Component{
componentDidMount() {
this.refs[‘box’].innerHTML=‘这是 div 盒子,通过 ref 获取’
}
render(){
return(

)
}
}
复制代码方式 2:回调函数,在dom节点或组件上挂载函数,函数的入参是dom节点或组件实例,达到的效果与字符串形式是一样的,都是获取其引用
class RefTwo extends React.Component{
componentDidMount() {
this.input.value=‘这是输入框默认值’;
this.input.focus();
}
render(){
return(
<input ref={comp => { this.input = comp; }}/>
)
}
}
复制代码方式 3:React.createRef()
React 16.3版本后,使用此方法来创建ref。将其赋值给一个变量,通过ref挂载在dom节点或组件上,该ref的current属性,将能拿到dom节点或组件的实例
class RefThree extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
}
componentDidMount(){
console.log(this.myRef.current);
}
render(){
return
}
}
复制代码方式 4:React.forwardRef
React 16.3版本后提供的,可以用来创建子组件,以传递ref
class RefFour extends React.Component{
constructor(props){
super(props);
this.myFourRef=React.createRef();
}
componentDidMount(){
console.log(this.myFourRef.current);
}
render(){
return
}
}
复制代码子组件通过React.forwardRef来创建,可以将ref传递到内部的节点或组件,进而实现跨层级的引用。forwardRef在高阶组件中可以获取到原始组件的实例.这个功能在技巧 18 会着重讲

https://www.jianshu.com/p/38e098829c3d
https://www.jianshu.com/p/aabe719adf57
https://www.jianshu.com/p/96a12dfa2cf8
https://www.jianshu.com/p/ea6eaf7aed86
https://www.jianshu.com/p/3746deb364fe
https://www.jianshu.com/p/8cc1b4f673e0
9.static 使用
场景:声明静态方法的关键字,静态方法是指即使没有组件实例也可以直接调用
export default class Nine extends React.Component {
static update(data) {
console.log(‘静态方法调用执行啦’)
}
render() {
return (


这是 static 关键字技能

);
}
}

Nine.update(‘2’)
复制代码注意:
1.ES6的class,我们定义一个组件的时候通常是定义了一个类,而static则是创建了一个属于这个类的属性或者方法
2.组件则是这个类的一个实例,component的props和state是属于这个实例的,所以实例还未创建
3.所以static并不是react定义的,而加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,所以也是无法访问到 this
4.getDerivedStateFromProps也是通过静态方法监听值,详情请见技巧 6
10.constructor和super
回顾:
1.谈这两个属性之前,先回顾一下ES6 函数定义方法
2.每一个使用class方式定义的类默认都有一个constructor函数, 这个函数是构造函数的主函数, 该函数体内部的this指向生成的实例
3.super关键字用于访问和调用一个对象的父对象上的函数
export default class Ten extends React.Component {
constructor() { // class 的主函数
super() // React.Component.prototype.constructor.call(this),其实就是拿到父类的属性和方法
this.state = {
arr:[]
}
}
render() {
return (


这是技巧 10

);
}
}
复制代码11.PropTypes
场景:检测传入子组件的数据类型
类型检查PropTypes自React v15.5起已弃用,请使用prop-types
方式 1:旧的写法
class PropTypeOne extends React.Component {
render() {
return (

{this.props.email}

{this.props.name}


);
}
}

PropTypeOne.propTypes = {
name: PropTypes.string, //值可为array,bool,func,number,object,symbol
email: function(props, propName, componentName) { //自定义校验
if (
!/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/.test(
props[propName]
)
) {
return new Error(
“组件” + componentName + “里的属性” + propName + “不符合邮箱的格式”
);
}
},
};
复制代码方法 2:利用 ES7 的静态属性关键字 static
class PropTypeTwo extends React.Component {
static propTypes = {
name:PropTypes.string
};
render() {
return (


{this.props.name}


);
}
}
复制代码12.使用类字段声明语法
场景:可以在不使用构造函数的情况下初始化本地状态,并通过使用箭头函数声明类方法,而无需额外对它们进行绑定
class Counter extends Component {
state = { value: 0 };

handleIncrement = () => {
this.setState(prevState => ({
value: prevState.value + 1
}));
};

handleDecrement = () => {
this.setState(prevState => ({
value: prevState.value - 1
}));
};

render() {
return (


{this.state.value}

    <button onClick={this.handleIncrement}>+</button>
    <button onClick={this.handleDecrement}>-</button>
  </div>
)

}
}
复制代码13.异步组件
1.场景:路由切换,如果同步加载多个页面路由会导致缓慢
2.核心 API:
loader:需要加载的组件
loading:未加载出来的页面展示组件
delay:延迟加载时间
timeout:超时时间
3.使用方法:
安装 react-loadable ,babel插件安装 syntax-dynamic-import. react-loadable是通过webpack的异步import实现的
const Loading = () => {
return

loading
;
};

const LoadableComponent = Loadable({
loader: () => import("…/…/components/TwoTen/thirteen"),
loading: Loading
});

export default class Thirteen extends React.Component {
render() {
return ;
}
}
复制代码4.Loadable.Map()
并行加载多个资源的高阶组件
14.动态组件
场景:做一个 tab 切换时就会涉及到组件动态加载
实质上是利用三元表达式判断组件是否显示
class FourteenChildOne extends React.Component {
render() {
return

这是动态组件 1
;
}
}

class FourteenChildTwo extends React.Component {
render() {
return

这是动态组件 2
;
}
}

export default class Fourteen extends React.Component {
state={
oneShowFlag:true
}
tab=()=>{
this.setState({oneShowFlag:!this.state.oneShowFlag})
}
render() {
const {oneShowFlag} = this.state
return (


显示组件{oneShowFlag?2:1}
{oneShowFlag?:}
);
}
}
复制代码15.递归组件
场景:tree组件
利用React.Fragment或者 div 包裹循环
class Item extends React.Component {
render() {
const list = this.props.children || [];
return (

{list.map((item, index) => {
return (
<React.Fragment key={index}>

{item.name}


{// 当该节点还有children时,则递归调用本身
item.children && item.children.length ? (
{item.children}
) : null}
</React.Fragment>
);
})}

);
}
}
复制代码16.受控组件和不受控组件
受控组件:组件拥有自己的状态
class Controll extends React.Component {
constructor() {
super();
this.state = { value: “这是受控组件默认值” };
}
render() {
return
{this.state.value}
;
}
}
复制代码不受控组件:组件无自己的状态,在父组件通过 ref 来控制或者通过 props 传值
class NoControll extends React.Component {
render() {
return
{this.props.value}
;
}
}
复制代码导入代码:
export default class Sixteen extends React.Component {
componentDidMount() {
console.log(“ref 获取的不受控组件值为”, this.refs[“noControll”]);
}
render() {
return (


<NoControll
value={“这是不受控组件传入值”}
ref=“noControll”
>

);
}
}
复制代码17.高阶组件
17.1 定义
就是类似高阶函数的定义,将组件作为参数或者返回一个组件的组件
17.2 实现方法
1.属性代理
import React,{Component} from ‘react’;

const Seventeen = WraooedComponent =>
class extends React.Component {
render() {
const props = {
…this.props,
name: “这是高阶组件”
};
return <WrappedComponent {…props} />;
}
};

class WrappedComponent extends React.Component {
state={
baseName:‘这是基础组件’
}
render() {
const {baseName} = this.state
const {name} = this.props
return


基础组件值为{baseName}

通过高阶组件属性代理的得到的值为{name}


}
}

export default Seventeen(WrappedComponent)
复制代码2.反向继承
原理就是利用 super 改变改组件的 this 方向,继而就可以在该组件处理容器组件的一些值
const Seventeen = (WrappedComponent)=>{
return class extends WrappedComponent {
componentDidMount() {
this.setState({baseName:‘这是通过反向继承修改后的基础组件名称’})
}
render(){
return super.render();
}
}
}

class WrappedComponent extends React.Component {
state={
baseName:‘这是基础组件’
}
render() {
const {baseName} = this.state
return


基础组件值为{baseName}


}
}

export default Seventeen(WrappedComponent);
复制代码18.元素是否显示
一般用三元表达式
flag?

显示内容
:’’
复制代码19.Dialog 组件创建
Dialog 应该是用的比较多的组件,下面有三种不同的创建方法
方式 1:通过 state 控制组件是否显示
class NineteenChildOne extends React.Component {
render() {
const Dialog = () =>
这是弹层1
;

return this.props.dialogOneFlag && <Dialog />;

}
}
复制代码方式 2:通过ReactDom.render创建弹层-挂载根节点外层
通过原生的createElement,appendChild, removeChild和react 的ReactDOM.render,ReactDOM.unmountComponentAtNode来控制元素的显示和隐藏
NineteenChild.jsx
import ReactDOM from “react-dom”;

class Dialog {
constructor(name) {
this.div = document.createElement(“div”);
this.div.style.width = “200px”;
this.div.style.height = “200px”;
this.div.style.backgroundColor = “green”;
this.div.style.position = “absolute”;
this.div.style.top = “200px”;
this.div.style.left = “400px”;
this.div.id = “dialog-box”;
}
show(children) {
// 销毁
const dom = document.querySelector("#dialog-box");
if(!dom){ //兼容多次点击
// 显示
document.body.appendChild(this.div);
ReactDOM.render(children, this.div);
}
}
destroy() {
// 销毁
const dom = document.querySelector("#dialog-box");
if(dom){//兼容多次点击
ReactDOM.unmountComponentAtNode(this.div);
dom.parentNode.removeChild(dom);
}
}
}
export default {
show: function(children) {
new Dialog().show(children);
},
hide: function() {
new Dialog().destroy();
}
};
复制代码nineteen.jsx
twoSubmit=()=>{
Dialog.show(‘这是弹层2’)
}

twoCancel=()=>{
Dialog.hide()
}
复制代码20.React.memo
作用:当类组件的输入属性相同时,可以使用 pureComponent 或 shouldComponentUpdate 来避免组件的渲染。现在,你可以通过把函数组件包装在 React.memo 中来实现相同的功能
import React from “react”;

function areEqual(prevProps, nextProps) {
/*
如果把 nextProps 传入 render 方法的返回结果与
将 prevProps 传入 render 方法的返回结果一致则返回 true,
否则返回 false
*/
if (prevProps.val === nextProps.val) {
return true;
} else {
return false;
}
}

// React.memo()两个参数,第一个是纯函数,第二个是比较函数
export default React.memo(function twentyChild(props) {
console.log("MemoSon rendered : " + Date.now());
return

{props.val}
;
}, areEqual);
复制代码21.React.PureComponent
作用:
1.React.PureComponent 和 React.Component类似,都是定义一个组件类。
2.不同是React.Component没有实现shouldComponentUpdate(),而 React.PureComponent通过props和state的浅比较实现了。
3.React.PureComponent是作用在类中,而React.memo是作用在函数中。
4.如果组件的props和state相同时,render的内容也一致,那么就可以使用React.PureComponent了,这样可以提高组件的性能
class TwentyOneChild extends React.PureComponent{ //组件直接继承React.PureComponent
render() {
return
{this.props.name}

}
}

export default class TwentyOne extends React.Component{
render(){
return (


<TwentyOneChild name={‘这是React.PureComponent的使用方法’}>

)
}
}
复制代码22.React.Component
作用:是基于ES6 class的React组件,React允许定义一个class或者function作为组件,那么定义一个组件类,就需要继承React.Component
export default class TwentyTwo extends React.Component{ //组件定义方法
render(){
return (
这是技巧22

)
}
}
复制代码23.在 JSX 打印 falsy 值
定义:
1.falsy 值 (虚值) 是在 Boolean 上下文中认定为 false 的值;
2.值有 0,"",’’,``,null,undefined,NaN
export default class TwentyThree extends React.Component{
state={myVariable:null}
render(){
return (
{String(this.state.myVariable)}

)
}
}
复制代码虚值如果直接展示,会发生隐式转换,为 false,所以页面不显示
24.ReactDOM.createPortal
作用:组件的render函数返回的元素会被挂载在它的父级组件上,createPortal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案
import React from “react”;
import ReactDOM from “react-dom”;
import {Button} from “antd”

const modalRoot = document.body;

class Modal extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement(“div”);
this.el.style.width = “200px”;
this.el.style.height = “200px”;
this.el.style.backgroundColor = “green”;
this.el.style.position = “absolute”;
this.el.style.top = “200px”;
this.el.style.left = “400px”;
}

componentDidMount() {
modalRoot.appendChild(this.el);
}

componentWillUnmount() {
modalRoot.removeChild(this.el);
}

render() {
return ReactDOM.createPortal(this.props.children, this.el);
}
}

function Child() {
return (


这个是通过ReactDOM.createPortal创建的内容

);
}

export default class TwentyFour extends React.Component {
constructor(props) {
super(props);
this.state = { clicks: 0 };
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(prevState => ({
clicks: prevState.clicks + 1
}));
}

render() {
return (


点击加1

点击次数: {this.state.clicks}






);
}
}

复制代码这样元素就追加到指定的元素下面啦
25.在 React 使用innerHTML
场景:有些后台返回是 html 格式字段,就需要用到 innerHTML 属性
export default class TwentyFive extends React.Component {
render() {
return (
<div dangerouslySetInnerHTML={{ __html: “这是渲染的 HTML 内容” }}>
);
}
}
复制代码26.React.createElement
语法:
React.createElement(
type,
[props],
[…children]
)
源码:
export default class TwentySix extends React.Component {
render() {
return (


{React.createElement(
“div”,
{ id: “one”, className: “two” },
React.createElement(“span”, { id: “spanOne” }, “这是第一个 span 标签”),
React.createElement(“br”),
React.createElement(“span”, { id: “spanTwo” }, “这是第二个 span 标签”)
)}

);
}
}
复制代码原理:实质上 JSX 的 dom 最后转化为 js 都是React.createElement
// jsx 语法

this is spanOne this is spanTwo

// 转化为 js
React.createElement(
“div”,
{ id: “one”, class: “two” },
React.createElement( “span”, { id: “spanOne” }, “this is spanOne”),
React.createElement(“span”, { id: “spanTwo” }, “this is spanTwo”)
);
复制代码27.React.cloneElement
语法:
React.cloneElement(
element,
[props],
[…children]
)
复制代码作用:这个方法的作用是复制组件,给组件传值或者添加属性
核心代码
React.Children.map(children, child => {
return React.cloneElement(child, {
count: _this.state.count
});
});
复制代码28.React.Fragment
作用:React.Fragment可以让你聚合一个子元素列表,并且不在DOM中增加额外节点
核心代码
render() {
const { info } = this.state;
return (


{info.map((item, index) => {
return (
<React.Fragment key={index}>
{item.name}

{item.age}

</React.Fragment>
);
})}

);
}
复制代码29.循环元素
内部没有封装像 vue 里面 v-for 的指令,而是通过 map 遍历
{arr.map((item,index)=>{
return(

{item.name}
{item.age}

)
})}
复制代码30.给 DOM 设置和获取自定义属性
作用:有些要通过自定义属性传值
export default class Thirty extends React.Component {
click = e => {
console.log(e.target.getAttribute(“data-row”));
};

render() {
return (


<div data-row={“属性1”} data-col={“属性 2”} onClick={this.click}>
点击获取属性


);
}
}
复制代码31.绑定事件
场景:交互就会涉及到事件点击,然后点击选中值传参也是一个很常见场景
import React from “react”;
import { Button } from ‘antd’

export default class Three extends React.Component {
state = {
flag: true,
flagOne: 1
};
click(data1,data2){
console.log(‘data1 值为’,data1)
console.log(‘data2 值为’,data2)
}
render() {
return (


<Button type=“primary” onClick={this.click.bind(this,‘参数 1’,‘参数 2’)}>点击事件

);
}
}

复制代码使用方法在源码 routes.js 有详细使用
32.React-Router
32.1 V3和 V4的区别
1.V3或者说V早期版本是把router 和 layout components 分开;
2.V4是集中式 router,通过 Route 嵌套,实现 Layout 和 page 嵌套,Layout 和 page 组件 是作为 router 的一部分;
3.在V3 中的 routing 规则是 exclusive,意思就是最终只获取一个 route;
4.V4 中的 routes 默认是 inclusive 的,这就意味着多个; 可以同时匹配和呈现.如果只想匹配一个路由,可以使用Switch,在 中只有一个 会被渲染,同时可以再在每个路由添加exact,做到精准匹配
Redirect,浏览器重定向,当多有都不匹配的时候,进行匹配
32.2 使用
import { HashRouter as Router, Switch } from “react-router-dom”;

class App extends React.Component{
render(){
const authPath = ‘/login’ // 默认未登录的时候返回的页面,可以自行设置
let authed = this.props.state.authed || localStorage.getItem(‘authed’) // 如果登陆之后可以利用redux修改该值
return (


{renderRoutes(routes, authed, authPath)}


)
}
}
复制代码V4是通过 Route 嵌套,实现 Layout 和 page 嵌套,Switch切换路由的作用
33.样式引入方法
方式 1:import 导入
import ‘./App.css’;
复制代码方式 2:内联方式
import React from ‘react’;

const Header = () => {

const heading = '头部组件'

return(
    <div style={{backgroundColor:'orange'}}>
        <h1>{heading}</h1>
    </div>
)

}

或者
import React from ‘react’;

const footerStyle = {
width: ‘100%’,
backgroundColor: ‘green’,
padding: ‘50px’,
font: ‘30px’,
color: ‘white’,
fontWeight: ‘bold’
}

export const Footer = () => {
return(


底部组件

)
}
复制代码34.动态绑定 className
原理:通过三元表达式控制 className 值
render(){
const flag=true
return (
<div className={flag?“active”:“no-active”}>这是技巧 34
)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值