1. useState
state 只在组件首次渲染的时候被创立
useState Hook: 容许咱们在函数组件中存储外部 state。
const [fruit, setFruit] = useState('banana'); // 构造语法
2. useEffect
useEffect Hook 可看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
React 组件中有两种常见副作用操作:须要革除的和不须要革除的。咱们来更认真地看一下他们之间的区别。
when?
euseEffect 容许在组件加载和更新时执行操作。从概念上说,咱们心愿有些操作在每次渲染之后执行 —— 但 React 的 class 组件没有提供这样的办法。即便咱们提取出一个办法,咱们还是要在两个中央调用它。如把副作用操作放到 componentDidMount 和 componentDidUpdate 函数中
// 在 class 组件中
componentDidMount() { // 首次渲染时
document.title = `You clicked ${this.sta 执行 te.count} times`;
}
componentDidUpdate() { // 更新时
document.title = `You clicked ${this.state.count} times`;
}
// 用 useEffect
useEffect(()=>{document.title = `You clicked ${count} times`;
)
Do what?
Effect 能够在 React 组件渲染后执行某些操作。当 React 渲染组件时,React 会保留你传递的函数(咱们将它称之为“effect”),并在更新完 DOM 后执行它。也就是说它在渲染之后和每次更新之后都会执行。React 保障了每次运行 effect 的同时,DOM 都曾经更新结束。咱们能够在 effect 中获取到最新的 变量值,变量值须要在函数的作用域内。这个过程在每次渲染时都会产生,包含首次渲染。
ps:传递给 useEffect 的函数在每次渲染中都会有所不同,这是刻意为之的。事实上这正是咱们能够在 effect 中获取最新的 count 的值,而不必放心其过期的起因。每次咱们从新渲染,都会生成新的 effect,替换掉之前的。某种意义上讲,effect 更像是渲染后果的一部分 —— 每个 effect“属于”一次特定的渲染。
在 class 组件中
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentDidUpdate(prevProps) {
// 勾销订阅之前的 friend.id
ChatAPI.unsubscribeFromFriendStatus(
prevProps.friend.id,
this.handleStatusChange
);
// 订阅新的 friend.id
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
然而用 useEffect 状况下
// Mount with {friend: { id: 100} } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange); // 运行第一个 effect
// Update with {friend: { id: 200} } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // 革除上一个 effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange); // 运行下一个 effect
// Update with {friend: { id: 300} } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // 革除上一个 effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange); // 运行下一个 effect
// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // 革除最初一个 effect
每个 effect 都能够返回一个革除函数。如此能够将增加和移除订阅的逻辑放在一起。它们都属于 effect 的一部分。React 会在组件卸载的时候执行革除操作。正如之前学到的,effect 在每次渲染的时候都会执行。这就是为什么 React 会在执行以后 effect 之前对上一个 effect 进行革除。
对于多个 effect 申明,React 将依照 effect 申明的程序顺次调用组件中的每一个 effect。
遗记正确天文 componentDidUpdate 是 React 利用中常见的 bug 起源,然而 useEffect 默认就会解决。它会在调用一个新的 effect 之前对前一个 effect 进行清理。此默认行为保障了一致性,防止了在 class 组件中因为没有解决更新逻辑而导致常见的 bug。
原理
Hook 应用了 JavaScript 的闭包机制,而不必在 JavaScript 曾经提供了解决方案的状况下,还引入特定的 React API。将 useEffect 放在组件外部让咱们能够在 effect 中间接拜访 count state 变量(或其余 props)。咱们不须要非凡的 API 来读取它 —— 它曾经保留在函数作用域中。
Effect 进行性能优化
因为 effect 在每次渲染后都执行清理或者执行 effect 可能会导致性能问题,在 class 组件中,咱们能够通过在 componentDidUpdate 中增加对 prevProps 或 prevState 的比拟逻辑解决:
componentDidUpdate(prevProps, prevState) {if (prevState.count !== this.state.count) {document.title = `You clicked ${this.state.count} times`;
}
}
effect 在批改时更新的办法,如果某些特定值在两次重渲染之间没有发生变化,你能够告诉 React 跳过对 effect 的调用,只有传递数组作为 useEffect 的第二个可选参数即可,如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),能够传递一个空数组([])作为第二个参数。这就通知 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不须要反复执行。想使用性能优化须要确保数组中蕴含了所有内部作用域中会随工夫变动并且在 effect 中应用的变量,
useEffect(() => {document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新
- Hooks 规定
1. 只在最顶层应用 Hook:不要在循环,条件或嵌套函数中调用 Hook,如果咱们想要有条件地执行一个 effect,能够将判断放到 Hook 的外部。
2. 只在 React 函数中调用 Hook
- 自定义 Hooks
共享组件之间的状态逻辑:风行的有:render props 和高阶组件,Hooks 能够让你在不减少组件的状况下解决雷同问题
如何实现 reducer 的状态治理:编写一个 useReducer 的 Hook,应用 reducer 的形式来治理组件的外部 state
function useReducer(reducer, initialState) {const [state, setState] = useState(initialState);
function dispatch(action) {const nextState = reducer(state, action);
setState(nextState);
}
return [state, dispatch];
}
// 在组件中应用
function Todos() {const [todos, dispatch] = useReducer(todosReducer, []);
function handleAddClick(text) {dispatch({ type: 'add', text});
}
// ...
- 思考
1. 渲染过程
2. React 怎么晓得 useState 对应的是哪个组件
3. 传递给 useEffect 的函数在每次渲染中都会有所不同
4. 组件更新的时候执行革除操作吗?卸载和革除的区别