HOOK:是一个特殊的函数组件,可以在函数组件内使用生命周期函数和state等特性的函数
useEffect()函数
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
//相当于componentWillUnmount()函数
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
在react组件中执行数据获取、订阅或者手动修改DOM,统称为“副作用“。useEffect就是给组件操作副作用的能力。它将componentDidMount、componentDidUpdate 和 componentWillUnmount合并在一起,成为一个api。
无需清除的effect
无需清除的操作:请求数据、手动改变DOM、记录日志等
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
}
默认情况下,effect会在每次渲染之后调用,包括第一次调用。
需要清除的effect
如果你的 effect 返回一个函数,React 将会在执行清除操作时调用它:
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
useState()函数
const [count, setCount] = useState(0);
//数组解构,useState返回一个数组
与class组件的区别:更新state变量,旧的state变量不会消失,新的state会替换它。
HOOK规则
1.只在最顶层使用HOOK
不要在循环、条件或者嵌套函数中使用HOOK,确保总是在你的react函数的最顶层调用他们。
2.只在react函数中使用HOOK,不要在普通函数中使用HOOK
function Form() {
// 1. Use the name state variable
const [name, setName] = useState('Mary');
// 2. Use an effect for persisting the form
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
// 3. Use the surname state variable
const [surname, setSurname] = useState('Poppins');
// 4. Use an effect for updating the title
useEffect(function updateTitle() {
document.title = name + ' ' + surname;
});
// ...
}
// ------------
// 首次渲染
// ------------
useState('Mary') // 1. 使用 'Mary' 初始化变量名为 name 的 state
useEffect(persistForm) // 2. 添加 effect 以保存 form 操作
useState('Poppins') // 3. 使用 'Poppins' 初始化变量名为 surname 的 state
useEffect(updateTitle) // 4. 添加 effect 以更新标题
// -------------
// 二次渲染
// -------------
useState('Mary') // 1. 读取变量名为 name 的 state(参数被忽略)
useEffect(persistForm) // 2. 替换保存 form 的 effect
useState('Poppins') // 3. 读取变量名为 surname 的 state(参数被忽略)
useEffect(updateTitle) // 4. 替换更新标题的 effect
// ...
当把HOOK操作都放在最顶层时,react是通过HOOK每次的调用顺序来对应state和effect的操作的。只要HOOK的调用顺序在多次渲染的时候能够保持一致,react就能正确的将内部state和对应的HOOK进行关联。
如果将HOOK放在if语句中
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
}
当if操作不执行时,渲染时跳过这个HOOK操作,就会使得HOOK的调用顺序发生变化,得到错误的结果。
自定义HOOK
REACT CLASS 无法将逻辑在组件中共享,HOOK解决了这个问题。HOOK令组件脱离UI,只将逻辑封装起来。
关于 eslint 和 useEffect 之间的错误提示:
React Hook useEffect has a missing dependency: ‘getFormData’. Either include it or remove the dependency array.
代码:
useEffect(() => {
getFormData()
}, [])
useEffect 和 useMemo 之间的区别
简单点说:useEffect是在render渲染之后才运行,useMemo是在render渲染之前执行的。
当Button
父组件:
function App() {
const [name, setName] = useState('名称')
const [content,setContent] = useState('内容')
return (
<>
<button onClick={() => setName(new Date().getTime())}>name</button>
<button onClick={() => setContent(new Date().getTime())}>content</button>
<Button name={name}>{content}</Button>
</>
)
}
子组件
function Button({ name, children }) {
function changeName(name) {
console.log('11')
return name + '改变name的方法'
}
const otherName = changeName(name)
return (
<>
<div>{otherName}</div>
<div>{children}</div>
</>
)
}
当父组件中,点击改变name和改变content时,子组件都会重新渲染,执行changeName函数。我们希望,当改变content时,不执行changeName函数。
用useMemo实现:
function Button({ name, children }) {
function changeName(name) {
console.log('11')
return name + '改变name的方法'
}
const otherName = useMemo(()=>changeName(name),[name])
return (
<>
<div>{otherName}</div>
<div>{children}</div>
</>
)
}
export default Button
当父组件的name放生变化时,才会执行changeName函数,改变content时,不会调用。
注:useMemo是在render页面渲染之前执行的,所以是在页面重新渲染otherName之前,就计算otherName的值,避免二次渲染,达到了性能优化的目的。
useEffect是在render渲染之后执行的。在name发生变化时,先render渲染页面,后执行useEffect,改变otherName之后,又重新渲染页面。