一、先说一下react
react是基于数据是不可变的(每次setState都会返回一个新数据),这也是为什么需要setState()来更新数据而不能使用像vue的this.state = newState的形式更新数据的原因,其实你用this.state=newState确实可以改数据,但是react不知道数据变了。
二、useMemo、useEffect的执行时机对比
useMemo和useCallback都会在组件第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行;并且这两个hooks都返回缓存的值,useMemo返回缓存的变量,useCallback返回缓存的函数。
三、useMemo的使用场景
使用过vue的话,你可以把它理解成vue里面的computed,是一种数据的缓存,而这个缓存依赖后面的第二个参数数组,如果这个数组里面传入的数据不变,那么这个useMemo返回的数据是之前里面return的数据。
在具体项目中,如果你的页面上展示的数据是通过某个(某些)state计算得来的一个数据,那么你每次这个组件里面无关的state变化引起的重新渲染,都会去计算一下这个数据,这时候就需要用useMemo(()=>{}, [])去包裹你的计算的方法体,这样那些无关的state改变引起的渲染不会重新计算这个方法体,而是返回之前计算的结果,达到一种缓存的效果。
export default function WithMemo() { const [count, setCount] = useState(1); const [val, setValue] = useState(''); const expensive = useMemo(() => { console.log('compute'); let sum = 0; for (let i = 0; i < count * 100; i++) { sum += i; } return sum; }, [count]); return <div> <h4>{count}-{expensive}</h4> {val} <div> <button onClick={() => setCount(count + 1)}>+c1</button> <input value={val} onChange={event => setValue(event.target.value)}/> </div> </div>; }
四、useCallback使用场景
useCallback跟useMemo比较类似,但它返回的是缓存的函数。
hooks组件state改变后会引起父组件的重新渲染,而每次重新渲染都会生成一个新函数,所以react子组件props在浅比较的时候就会认为props改变了,引起子组件不必要的渲染。
使用场景是:有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新。
为什么useCallback需要配合React.memo来使用?
react的Hooks组件对props的浅比较是在memo里面比较的(类组件是在shouldComponentUpdate里面),如果没有memo,那么你使用useCallback就没啥意义,反而浪费性能(因为useCallback来包裹函数也是需要开销的)。因为子组件还是会重新渲染。
function APP() { const [value, setValue] = useState(123) const [otherValue, setOtherValue] = useState(999) const changeValue = useCallback(() => { setValue(value => value+1) }, []) console.log('APP'); return ( <div> <div>与Message渲染无关的数据==={otherValue}</div> <br /> <button onClick={() => setOtherValue(value => value-=5)}>改变无关的数据</button> <br /> <br /> <Message value={value} changeValue={changeValue} /> </div> ) } const Message = memo( function Message({value, changeValue}) { console.log('Message'); return ( <div> <button onClick={changeValue}>改变有关数据</button> <p>与Message渲染有关的数据{value}</p> </div> ) } )