在设计 React 组件时,要注意以下原则:
保持接口小,props 数量要少;
根据数据边界来划分组件,充分利用组合(composition);
把 state 往上层组件提取,让下层组件只需要实现为纯函数。
任何一个复杂组件都是从简单组件开始的,一开始我们在 render 函数里写的代码不多,但是随着逻辑的复杂,JSX 代码越来越多,于是,就需要拆分函数中的内容。
避免 renderXXXX 函数
给回调函数类型的 props 加统一前缀
使用 propTypes 来定义组件的 props
功能正常;
代码整洁;
高性能。
所以,从达到“代码整洁”的目的来说,应该每个组件都有一个独立的文件,然后这个文件用 export default 的方式导出单个组件。
在早期版本中,React.Component 的构造函数参数有两个,第一个是 props,第二个是 context,如果忽略掉 context 参数,那么这个组件的 context 功能就不能正常工作,不过,现在React的行为已经变了,第二个参数传递不传递都能让context正常工作,看起来React.Component 的构造函数只有第一个参数被用到,但是,没准未来还会增加新的参数呢,所以,以不变应万变的方法,就是使用扩展操作符(spread operator)来展开 arguments,这样不管 React 将来怎么变,这样的代码都正确。
constructor() {
super(...arguments); //永远正确!
}
扩展操作符的作用,在 React 开发中会经常用到,在 JSX 中展开 props 的时候会用到。
首先,明确一点,尽量不要在 JSX 中写内联函数(inline function),比如这样写,是很不恰当的:
<ControlButtons
activated={this.state.isStarted}
onStart={() => { /* TODO */}}
onPause={() => { /* TODO */}}
onReset={() => { /* TODO */}}
onSplit={() => { /* TODO */}}
/>
当然,按照上面那种写法,也可以完成程序的功能,但是,会带来性能的代价。首先,每一次渲染这段 JSX,都会产生全新的函数对象,这是一种浪费;其次,因为每一次传给 ControlButtons 的都是新的 props,这样 ControlButtons 也无法通过 shouldComponentUpdate 对 props 的检查来避免重复渲染。
react 傻瓜组件优化方案
React.memo(rederFun, equalFun)
React.memo的确支持第二个函数参数作比较,只是这个函数参数只能访问到props,没有state,这也是memo的一个局限。
“高阶组件”名为“组件”,其实并不是一个组件,而是一个函数,只不过这个函数比较特殊,它接受至少一个 React 组件为参数,并且能够返回一个全新的 React 组件作为结果,当然,这个新产生的 React 组件是对作为参数的组件的包装,所以,有机会赋予新组件一些增强的“神力”。
上面的函数 withDoNothing 就是一个高阶组件,作为一项业界通用的代码规范,高阶组件的命名一般都带 with 前缀,命名中后面的部分代表这个高阶组件的功能。
就如同 withDoNothing 这个名字所说的一样,这个高阶组件什么都没做,但是从中可以看出高阶组件的基本代码套路。
高阶组件不能去修改作为参数的组件,高阶组件必须是一个纯函数,不应该有任何副作用。
高阶组件返回的结果必须是一个新的 React 组件,这个新的组件的 JSX 部分肯定会包含作为参数的组件。
高阶组件一般需要把传给自己的 props 转手传递给作为参数的组件。
高阶组件只需要返回一个 React 组件即可,没人规定高阶组件只能接受一个 React 组件作为参数,完全可以传入多个 React 组件给高阶组件。
如果要做一个最简单的什么增强功能都没有的高阶组件,也必须要写下面这样的代码:
const withExample = (Component) => {
const NewComponent = (props) => {
return
}
NewComponent.displayName = withExample(${Component.displayName || Component.name || 'Component'})
;
return NewCompoennt;
};
最后,使用高阶组件,一定要非常小心,要避免重复产生 React 组件,比如,下面的代码是有问题的:
const Example = () => {
const EnhancedFoo = withExample(Foo);
return
}
像上面这样写,每一次渲染 Example,都会用高阶组件产生一个新的组件,虽然都叫做 EnhancedFoo,但是对 React 来说是一个全新的东西,在重新渲染的时候不会重用之前的虚拟 DOM,会造成极大的浪费。
正确的写法是下面这样,自始至终只有一个 EnhancedFoo 组件类被创建:
const EnhancedFoo = withExample(Foo);
const Example = () => {
return
}
所以,当需要重用 React 组件的逻辑时,建议首先看这个功能是否可以抽象为一个简单的组件;如果行不通的话,考虑是否可以应用 render props 模式;再不行的话,才考虑应用高阶组件模式。