React Hook
不编写class的情况下使用state等React特性
即将一些特性抽离进行模块化使用
所有可以在函数式组件中使用
useState
参数为初始值 返回当前state和更新state函数。
函数调用后变量消失,而state的变量会被React保留。
const [count, setCount] = useState(0); 初始state
<button onClick={() => setCount(count + 1)}>
可以直接使用声明的变量。替代this.state.<变量>
使用过程:
1.引入React中的useState Hook 可以存储内部state
2.调用useState声明state变量
返回数组([0] : 变量名,[1] : 更新变量函数)
3.使用更新函数传递一个新值,React会重新渲染组件,并把最新的值传递给该组件。
注意事项:和class中this.setState不同,useState是替换而不是合并。可以使用展开运算符达到合并更新对象。
setState(prevState=>
{
// 也可以使用 Object.assign
return
{...
prevState
,...
updatedValues
};
});
惰性初始state 在useState里传入函数返回一个初始值。此函数只会在初始渲染时被调用。
useEffect
每次渲染之后都会执行副作用操作(数据获取,设置订阅,手动更新React组件中DOM)
类似于React class 生命周期中
componentDidMount, componentDidUpdate, componentWillUnmount
的组合避免同一个生命周期包含不相关的逻辑,又把相关逻辑分离到不同的方法中。!!!按照代码用途分离他们。
effect的执行时机
useEffect会在浏览器完成布局和绘制之后,在一个延迟事件中被调用。
执行原理:告诉React组件渲染后执行哪些操作,React会保存你传递的函数(effect) ,执行DOM更新后再次调用effect。
!!!传递给useEffect的函数每次渲染都不同,effect获取最新的state变量。每次渲染都会生成一个新的effect,替换之前的effect。让effect成为渲染结果的一部分。
内部调用原因:利用JS闭包原理直接访问state变量,无需引入其他API。
清除effect:
effect返回一个函数,当执行清除操作时调用它。
执行当前effect时,对上一个effect进行清除。
为什么每次渲染都要执行effect:
避免class组件中没有处理更新逻辑导致bug
通过跳过Effect进行性能优化,class组件中使用componentDidUpdate 添加prevProps 或 prevState 比较逻辑解决。
useEffect(()
=>
{
document
.title
=
`You clicked ${count} times`;
}, [count](依赖数组)); // 仅在 count 更改时更新
只运行一次effect(仅挂载和卸载时执行),传递一个空数组( [] ) 作为第二个参数。
Hook规则
不要再循环,条件或者嵌套函数中调用Hook。
确保总是在React函数的最顶层以及任何return之前调用。
React如何对应state和useState:
依赖与Hook调用的顺序,每次渲染Hook调用顺序都是相同的。
提取自定义Hook(复用)
使用Hook定义的一个函数,返回对应的值。!!!use开头。
useContext
const value = useContext(MyContext)
!!!参数必须是context本身
原理:
当组件上一层最近的<MyContext.Provider>更新时,该Hook会触发重渲染,并使用最新传递给MyContext的 provider的context value值,
即使祖先使用React.memo 或 shouldComponentUpdate。
useContext(MyContext) 可以读取context的值以及订阅context的变化。仍然需要上层组件树使用<MyContext.Provider>为下层组件提供context。
useReducer
接受一个形如(state,aciton)=>newState 的reducer,并返回当前的state以及与其配套的dispatch方法。
- 适用于场景
- state逻辑较复杂且包含多个子值。
- 下一个state依赖前一个state。
- 对深更新进行优化,向子组件传递一个dispatch而不是一个回调函数。
使用方法:
声明reducer (state, action){switch(action.type)}(不同情况,分类处理)
初始化 const [sate, dispatch] = useReducer(reducer, initialState)
使用 dispatch({ type:’condition1’})
惰性初始化
将init函数作为useReducer的第三个参数传入。
init函数返回初始化对象。{count: initialCount}
useCallback
返回一个memoized回调函数(参数1:内联回调函数,参数2:依赖项参数)。
回调函数仅在依赖项改变时才会更新。可以优化使用引用相等性去避免非必要渲染。(例如shouldComponentUpdate的子组件)
useCallback(fn, deps) 相当于useMemo( () =>fn, deps)
!!!注意事项
依赖项数组不会作为参数传给回调函数。
useCallback 的真正目的还是在于缓存了每次渲染时 inline callback 的实例,这样方便配合上子组件的 shouldComponentUpdate 或者 React.memo 起到减少不必要的渲染的作用
useMemo
返回一个memoized值
const memoizedValue = useMemo(
() => computeExpensiveValue(a,b), [a, b]);创建函数,依赖项数组
依赖项发生改变时,重新计算memoized值。
优化:
避免每次渲染都进行高开销的计算。
没有设置依赖项数组,则每次渲染计算新值。
useRef
返回一个可变的ref对象,其 .current 属性初始化为传入参数。
返回ref对象在组件的整个生命周期内保持不变。
const refContainer = useRef(initalValue);
本质:
useRef就是在.current属性上保存一个可变值的”盒子”。
!!!ref对象内容发生改变时,useRef不会通知。变更.current属性不会引发重新渲染。如果想要在React绑定或解绑DOM节点的ref时运行代码,需要使用回调ref。
useImperativeHandle
使用ref时自定义暴露给父组件的实例值。
避免ref命令式代码。
useImperatveHandle(ref, createHandle, [deps])
useLayoutEffect
与useEffect相同,但它会在所有DOM变更之后同步调用efect。
使用它来读取DOM布局并同步触发重渲染。
!!!服务端渲染无法使用。将代码转移到useEffect中,或时将组件延迟到客户端渲染完成后显示。(useLayoutEffect执行前HTML都显示错乱)
服务端渲染HTML排除依赖布局组件使用showChild && <Child/>条件渲染。useEffect(() => {setShowChild(true);}, []) 延迟展示组件。
useDebugValue
useDebugValue(value)
可用与在React开发者工具中显示自定义hook标签