React已经使用了一段时间,该去记录一些东西了
1. 什么是Hooks?
- 可以让你在函数组件里“钩入”React state及生命周期等特性的函数
- 不能在class组件中使用
1.1 有条件的渲染hooks
useEffect(function persistForm() {
// 👍 将条件判断放置在 effect 中
if (name !== '') {
localStorage.setItem('formData', name);
}
});
2. useEffect
- 为组件添加了操作副作用的能力;
- 告诉React在完成对DOM的更改后运行你的“副作用”函数
- 返回一个函数来指定如何“清除”副作用
- 副作用?
- React 组件中执行过数据获取、订阅或者手动修改过 DOM
2.1 useEffect 做了什么?
- 告诉组件需要在渲染后执行某些操作
- React 会等待浏览器完成画面渲染之后才会延迟调用
useEffect- useEffect的函数在浏览器完成布局与绘制之后,在一个延迟事件中被调用
2.2 useEffect 参数
function Example({ someProp }) {
function doSomething() {
console.log(someProp); }
useEffect(() => {
doSomething();
}, []); // 🔴 这样不安全(它调用的 `doSomething` 函数使用了 `someProp`)}
useEffect(() => {
function doSomething() {
console.log(someProp); }
doSomething();
}, [someProp]); // ✅ 安全(我们的 effect 仅用到了 `someProp`)
2.3 effect 的依赖频繁变化,我该怎么办?
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1); // 这个 effect 依赖于 `count` state }, 1000);
return () => clearInterval(id);
}, []); // 🔴 Bug: `count` 没有被指定为依赖
在 `setInterval` 的回调中,`count` 的值不会发生变化。
因为当 effect 执行时,我们会创建一个闭包,并将 `count` 的值被保存在该闭包当中,且初值为 `0`。
每隔一秒,回调就会执行 `setCount(0 + 1)`,因此,`count` 永远不会超过 1。
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1); // 这个 effect 依赖于 `count` state }, 1000);
return () => clearInterval(id);
}, []); // 🔴 Bug: `count` 没有被指定为依赖
指定 `[count]` 作为依赖列表就能修复这个 Bug,但会导致每次改变发生时定时器都被重置。
事实上,每个 `setInterval` 在被清除前(类似于 `setTimeout`)都会调用一次。
但这并不是我们想要的。
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + 1); // ✅ 在这不依赖于外部的 `count` 变量
}, 1000);
return () => clearInterval(id);
}, []); // ✅ 我们的 effect 不使用组件作用域中的任何变量
setState 的函数式更新形式
}
3. useState
3.1 函数式更新
如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给
setState。
- 该函数将接收先前的 state,并返回一个更新后的值
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + 1); // ✅ 在这不依赖于外部的 `count` 变量
}, 1000);
return () => clearInterval(id);
}, []); // ✅ 我们的 effect 不使用组件作用域中的任何变量
4. useContext
接收一个 context 对象(
React.createContext的返回值)并返回该 context 的当前值
- 当前的 context 值由上层组件中距离当前组件最近的
<MyContext.Provider>的valueprop 决定。- 当组件上层最近的
<MyContext.Provider>更新时,该 Hook 会触发重渲染
const value = useContext(MyContext);
4. useMemo
传入
useMemo的函数会在渲染期间执行
- 如果没有提供依赖项数组,
useMemo在每次渲染时都会计算新的值。
React共享组件之间的状态逻辑的方法
处理深度更新的推荐模式
useContext + useReducer结合使用,向下传递
dispatch
import React, { useReducer, useContext } from 'react'
const MyContext = React.createContext()
const Child = () => {
const dispatch = useContext(MyContext)
return <MyContext.Consumer>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
</MyContext.Consumer>
}
const contextComp = () => {
const initState = {
count: 0
}
const reducerFn = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 }
case 'decrement':
return { count: state.count - 1 }
default:
throw new Error()
}
}
const [state, dispatch] = useReducer(reducerFn, initState)
return (
<MyContext.Provider value={dispatch}>
Count: {state.count}
<Child />
</MyContext.Provider>
)
}
export default contextComp
为什么我会在我的函数中看到陈旧的 props 和 state ?
如果你刻意地想要从某些异步回调中读取 最新的 state
import React, { useState, useRef, useEffect } from 'react'
function Example () {
const myRef = useRef(null)
const [count, setCount] = useState(0)
useEffect(() => {
myRef.current = count
}, [count])
function handleAlertClick () {
setTimeout(() => {
alert('You clicked on: ' + myRef.current)
}, 3000)
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<button onClick={handleAlertClick}>
Show alert
</button>
</div>
)
}
export default Example
怎样阻止函数被调用太快或者太多次?
- 节流:throttle(this.handleClick, 1000);
-
节流阻止函数在给定时间窗口内被调不能超过一次
-
基于时间的频率来进行抽样更改 (例如
_.throttle)
- 防抖:debounce(this.emitChange, 250)
- 防抖确保函数不会在上一次被调用之后一定量的时间内被执行
- 一段时间的不活动之后发布更改 (例如
_.debounce)
requestAnimationFrame节流:基于 requestAnimationFrame 的抽样更改 (例如raf-schd)
什么是 “React Fiber”?
它的主要目的是使 Virtual DOM 可以进行增量式渲染