使用Hook的动机
-
Hook 使你在无需修改组件结构的情况下复用状态逻辑。在Hook之前,我们可以使用render props和高阶组件来添加可复用的状态逻辑。
render props就是在react组件之间使用一个值为函数的prop共享代码技术,组件接收一个返回React元素的函数,并在组件内部调用这个函数完成渲染逻辑。(这个prop的名字叫render或其他名字),而且也不一定要放到JSX元素的attribute列表中,也可以放在元素标签内部。
高阶组件(Higher Order Component)是参数为组件,返回值为新组件的函数。HOC将组件包装在容器组件中来组成新的组件,来完成一些可复用的逻辑。
但这两个方案需要重新组织组件的结构,可能使代码难以理解。使用Hook可以从组件中提取状态逻辑,使这些逻辑可以单独测试且复用,并无需修改组件结构。
-
我们维护组件时,随着时间推移,组建的生命周期函数往往变得臃肿,会有很多不相关的状态逻辑。Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据),而并非强制按照生命周期划分。
-
Hook 使你在非 class 的情况下可以使用更多的 React 特性。无需理解class
Hook的特性
Hook是钩子的意思,就是我们使用函数式组件时,尽量写成纯函数,如果需要React state和生命周期等特性的函数,就使用Hook完成这些原本在class中才能实现的需求。Hook是
- 完全可选的
- 100%向后兼容
- 现在可用
- 没有计划从React中移除class
Hook使用规则
- 只能在函数最外层调用Hook,不要再循环、条件判断或子函数中调用。
React怎么知道哪个state对应哪个useState,答案是靠Hook执行的顺序,如果我们在条件语句中使用hook,那么很有可能导致前后两次hooks执行顺序发生改变,导致bug产生。如果我们想要有条件地执行一个 effect,可以将判断放到 Hook 的内部。
- 只能在React的函数式组件中调用,不要在其他JavaScript函数中调用。
React中几种常见的Hook
useState
相当于类组件中的state。
当我们调用useState时,我们传入一个参数,作为这个state的初始值,它返回一个有两个元素的数组,分别代表当前state的值以及更新这个state的函数,比如[count, setCount] = useState(0)
,通过数组解构,我们得到两个变量,count是这个state的变量名,我们可以调用setCount更新count这个state。
使用useState时,应该使用单个state变量还是使用一个对象打包所有state?
React官方推荐把 state 切分成多个 state 变量,因为每次更新一个state,会用新值整个替换旧值,不像class组件的setState那样合并。因此如果非要用一个对象包含所有state,state的更新要这样实现:
const [state, setState] = useState({ left: 0, top: 0, width: 100, height: 100 });
...
/ 展开 「...state」 以确保我们没有 「丢失」 width 和 height
setState(state => ({ ...state, left: e.pageX, top: e.pageY }));
useEffect
useEffect(() => {...}, []);
数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用。我们可以使用useEffect完成在函数式组件中这些功能。
清除操作:只需在返回值中返回一个函数,那么React会在组件卸载的时候执行清除操作并调用它,比如取消订阅。
默认情况下,useEffect会在每次渲染后执行,如果要通知React跳过对effect的调用,即不要每次渲染都调用,我们可以传递数组作为第二个参数,比如我们传入[count]作为第二个参数,那么只有当count发生改变时,这个副作用函数才会被执行。
//相当于componentDidMount和componentWillUnmount
useEffect(() => {...}, [])
//会紧紧盯着count,只要count值改变,就会执行
useEffect(() => {...}, [count])
//默认情况,相当于componentDidMount和componentDidUpdate
useEffect(() => {...})
useContext
在组件之间共享状态的钩子。在React中,如果我们要传递变量,可以使用props属性向下传递给子组件。这种方法很简单,但当我们想把变量传递给子组件的子组件时,就需要使用子组件的props再往下传递,这样就形成了props的深度注入。随着props注入越深,组件更新的频率也越来越高,UI效率也就越来越慢。
而useContext就是来解决非父子组件的数据共享问题的。
假设说我们需要全局共享一个变量,username。
第一步,我们要利用React Context API,在组件外部创建一个context,并传入默认初始值。
const defaultContextValue = { username: 'sxx' };
const appContext = React.createContext(defaultContextValue)
然后,为了使App组件和其子组件能共享这个username,我们要用appContext.Provider把整个render函数包裹起来。并且要把defaultContextValue注入到value属性中。
<appContext.Provider value={defaultContextValue}>
<App />
</appContext.Provider>
接下来,我们就可以在他的子孙组件中访问到username这个变量了。
有两种方法:
- 利用appContext.Consumer组件,在组件内部使用花括号,使用箭头函数在其内部共享数据:
import { appContext } from ...
...
<appContext.Consumer>
{(value) => {
{/*在这里可以访问到全局的username啦*/}
<h1>{value.username}</h1>
}}
</appContext.Consumer>
- 使用React Hook。利用useContext钩子函数,我们不用改变代码的结构,能很方便的在组件中获取数据:
import { useContext } from 'react';
const value = useContext(appContext);
//接着就可以在return中直接使用value了
useContext这个hook极大的减少了模板代码,降低了代码层级,也消灭了多个consumer嵌套的可能性。
useReducer
const [state, dispatch] = useReducer(reducer, initialState);
这个钩子接收一个reducer和initialState为参数,返回当前状态和dispatch action的函数,可以在不使用redux的情况下,管理数据状态。
const myReducer = (state, action) => {
switch(action.type) {
case('countUp'):
return {
...state,
count: state.count + 1
}
default:
return state;
}
}
//组件代码
function App() {
const [state, dispatch] = useReducer(myReducer, { count: 0 });
return (
<div className="App">
<button onClick={() => dispatch({ type: 'countUp' })}>
+1
</button>
<p>Count: {state.count}</p>
</div>
);
}
自定义Hook
自定义 Hook 的命名以use开头,不需要具有特殊的标识。我们可以自由的决定它的参数是什么,以及它应该返回什么(如果需要的话)。
参考资料:
- React Hook官方文档
- 阮一峰的网络日志——React Hooks 入门教程
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!