携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情
类组件和函数组件
类组件-容器组件
所谓类组件,就是基于 ES6 Class 这种写法,通过继承 React.Component 得来的 React 组件。以下是一个典型的类组件:
class DemoClass extends React.Component {
// 初始化类组件的 state
state = {
text: ""
};
// 编写生命周期方法 didMount
componentDidMount() {
// 省略业务逻辑
}
// 编写自定义的实例方法
changeText = (newText) => {
// 更新 state
this.setState({
text: newText
});
};
// 编写生命周期方法 render
render() {
return (
<div className="demoClass">
<p>{this.state.text}</p>
<button onClick={this.changeText}>点我修改</button>
</div>
);
}
}
什么是函数组件/无状态组件(Function Component/Stateless Component) 函数组件顾名思义,就是以函数的形态存在的 React 组件。早期并没有 React-Hooks 的加持,函数组件内部无法定义和维护 state,因此它还有一个别名叫“无状态组件”。以下是一个典型的函数组件:
function DemoFunction(props) {
const { text } = props
return (
<div className="demoFunction">
<p>{`function 组件所接收到的来自外界的文本内容是:[${text}]`}</p>
</div>
);
}
函数组件与类组件的对比:无关“优劣”,只谈“不同”
我们先基于上面的两个 Demo,从形态上对两种组件做区分。它们的区别就包括但不限于:
-
类组件需要继承 class,函数组件不需要;
-
类组件可以访问生命周期方法,函数组件不能;
-
类组件中可以获取到实例化后的 this,并基于这个 this 做各种各样的事情,而函数组件不可以;
-
类组件中可以定义并维护 state(状态),而函数组件不可以; .....
总结:
- class组件使用时,使用了类组件的部分特点(生命周期,等等): a. 封装:将一类属性或者方法聚拢到一个class里; b. 继承: 新增的class组件可以直接继承现有class组件,实现对对上述封装方法的复用。
- 函数组件在没有hooks时,虽然不能维护自己的state,但也能完成一些复杂交互,比如交互事件等等,而且相对于类组件更加的简洁灵活。
- 最后,函数组件会捕获 render 内部的状态,这是两类组件最大的不同。
参考文章:函数组件和类组件的不同
- 函数组件更加契合 React 框架的设计理念。
UI = render(data) state data == props hooks useState()
UI = f(data)
React本质做的事情是把 数据转为虚拟DOM树,再由不同平台的render去渲染成实际UI,比如H5使用的是ReactDomRender,RN使用的是ReactNativeRender,函数组件更契合React的本质。
函数组件的使用
使用原则:
- 不要在函数组件外使用
- 不能在循环和判断或者嵌套函数中使用
function f() { return (
) }function useHooks () {
}
自定义hooks
对于有使用 useState 或者 useEffect 的可以考虑,其余的可以单独拆分 util
deps
问答
- 直接放在函数体内,和放在 useEffect 的回调的有什么区别?
函数体是在每次render时就执行,useEffect代表的是副作用,是在render完成以后捕获state或者props再执行。副作用一定是和当前 render 的 结果没关系的,而只是 render 完之后做的一些额外的事情。
const add = p => p+1
useEffect(() => { // todo // 为了捕获当前的state
// state/props/usememo/usecalback
setInterval(() => { // 0 // render 捕获 setCount(add) }, 1000) }, [])
- 下面2种写法哪种优化更好?
useCallback 缓存一个函数
const handleIncrement = useCallback(() => setCount(count + 1), [count]);
const handleIncrement = useCallback(() => {
// ddd
setCount(q => q + 1)
}, []);
setState(previousState => state)
- 不要把 state 当做变量用
我们需要遵循一个原则,即:在保证 State 完整性的同时, 也要保证它的最小化。就是说,某些数据如果能从已有的 State 中计算得到,那么我们就应该始终在用的时候去计算,而不要把计算的结果存到某个 State 中。这样的话,才能简化我们的状态处理逻 辑。
-
useReducer 应用场景
-
受控组件和非受控组件,HCZ搜索之前搜索框的问题。
-
条件判断,wrapper
function Modal({visible, ...props}) {
// 当 visible 为 false时隐藏
if(!visible) reurn false // 会报错,判断不能影响每次的hooks
const [state, setState] = useState(0)
// 这里可能有一大块需要计算的逻辑
if(!visible) reurn false // 放这里上面的一大块逻辑还是会执行
return <div></div>
}
// 使用HOC方式包裹
function ModalWrapper ({visible, ...props}) {
if(!visible) reurn false
return <Modal {...props}/>
}
- render props 就是把一个 render 函数作为属性传递给某个组件,由这个组件去执行这个函数从而 render 实际的内容。render props 适合于UI的复用,hooks适合数据的复用。
// render props 的写法
function CounterRenderProps ({ children }) {
const [count, setCount] = useState(0)
const increment = useCallback(() => {
setCount(count + 1)
}, [count])
const decrement = useCallback(() => {
setCount(count + 1)
}, [count])
return children({ count, increment, decrement })
}
// 使用
function CounterRenderPropsExample() {
return <CounterRenderProps>
{ ({count, increment, decrement}) => (
<div>
<p>counter: {count}</p>
<div>
<button onClick={increment}>increment</button>
<button onClick={decrement}>decrement</button>
</div>
</div>
) }
</CounterRenderProps>
}
// 自定义Hooks
const useCounter = (initCounter = 0) {
const [count, setCount] = useState(initCounter)
const increment = useCallback(() => {
setCount(count + 1)
}, [count])
const decrement = useCallback(() => {
setCount(count + 1)
}, [count])
return [count, { increment, decrement }]
}
// 自定义hooks的使用
function CounterRenderPropsExample() {
const [count, { increment, decrement }] = useCounter(0)
<div>
<p>counter: {count}</p>
<div>
<button onClick={increment}>increment</button>
<button onClick={decrement}>decrement</button>
</div>
</div>
}
hooks 侧重业务的逻辑,自己不生产UI。Hooks 仅能替代纯数据逻辑的 render props。如果有 UI 展示的逻辑需要重用,那么我们还是必须借助于 render props 的逻辑。