Open ZhengXingchi opened 4 years ago
useRef在每次组件渲染的时候不会发生变化,类似于指针this,其只会在属性current上面发生重新赋值。一般配合useEffect用于锁住最新的state。
可以用于封装previousprops(主要理解react的useEffect在组件渲染render之后执行) 网上的usePrevious
import { useEffect,useRef } from "react"
const usePrevious=(value)=>{
const ref=useRef()
useEffect(()=>{
ref.current=value;
},value)
return ref.current
}
import React, { useState, useRef, useEffect,useReducer } from "react";
import "./styles.css";
export default function ShoppingList(){
const inputRef=useRef()
const [items,dispatch]=useReducer((state,action)=>{
switch(action.type){
case 'add':
return [
...state,
{
id:state.length,
name:action.name
}
]
case 'remove':
return state.filter((_,index)=>index!=action.index)
case 'clear':
return []
default:
return state
}
},[])
function handleSubmit(event) {
event.preventDefault();
dispatch({
type: 'add',
name: inputRef.current.value
});
inputRef.current.value = '';
}
return (
<>
<form onSubmit={handleSubmit}>
<input ref={inputRef} />
</form>
<button
className="button is-danger"
onClick={
() => dispatch({ type: 'clear' })
}>clear</button>
<ul>
{items.map((item, index) => (
<li className="section" key={item.id}>{item.name}
<button className="button" onClick={
() => dispatch({ type: 'remove', index })
}>X</button>
</li>
))}
</ul>
</>
)
}
React.memo 和 React.PureComponent 类似, React.PureComponent 在类组件中使用,而React.memo 在函数组件中使用
import React, { useState, useRef, useEffect,useReducer } from "react";
import "./styles.css";
const Counter =React.memo( ({ value, children }) => {
console.log('Render: ', children)
return (
<div>
{children}: {value}
</div>
)
})
const App = () => {
const [count1, setCount1] = React.useState(0)
const [count2, setCount2] = React.useState(0)
const increaseCounter1 = () => {
setCount1(count1 => count1 + 1)
}
return (
<>
<button onClick={increaseCounter1}>Increase counter 1</button>
<Counter value={count1}>Counter 1</Counter>
<Counter value={count2}>Coutner 2</Counter>
</>
)
}
export default App
如果不加React.memo,两个Counter都会console.log出render来。React.memo 浅层对比 prop 和 state 的方式来实现了该函数。现在开始第二个计数器将不会重新渲染了,由于 prop 没有发生改变。 在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树。 如要避免不必要的子组件的重渲染,你需要在所有可能的地方使用 PureComponent,或是手动实现 shouldComponentUpdate 方法。
useCallback 和 useMemo 参数相同,第一个参数是函数,第二个参数是依赖项的数组。主要区别是 React.useMemo 将调用 fn 函数并返回其结果,而 React.useCallback 将返回 fn 函数而不调用它。
const Me = ({girlFriendWords}) => {
// Provided that girlFriendWords is a string
const myReply = decideWhatToSay (girlFriendWords)
return <p>{myReply}</p>
}
代码中计算 myReply 值,默认每次组件渲染的时候都会重新执行
const Me = ({girlFriendWords}) => {
// Provided that girlFriendWords is a string
const myReply = React.useMemo(() => decideWhatToSay (girlFriendWords), [girlFriendWords])
return <p>{myReply}</p>
}
使用 React.useMemo 通过 [girlFriendWords] 作为依赖项,当依赖的值发生改变,函数才会重新执行 decideWhatToSay
使用场景是:有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;
import React, { useMemo, useCallback } from "react"
let Counter = ({ value, children, onClick }) => {
console.log('Render: ', children)
return (
<div onClick={onClick}>
{children}: {value}
</div>
)
}
Counter = React.memo(Counter)
const App = () => {
const [count1, setCount1] = React.useState(0)
const [count2, setCount2] = React.useState(0)
const increaseCounter1 = () => {
setCount1(count1 => count1 + 1)
}
const increaseCounter2 = () => {
setCount2(count2 => count2 + 1)
}
return (
<>
<Counter value={count1} onClick={increaseCounter1}>Counter 1</Counter>
<Counter value={count2} onClick={increaseCounter2}>Coutner 2</Counter>
</>
)
}
export default App
但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新。
import React, { useMemo, useCallback } from "react"
let Counter = ({ value, children, onClick }) => {
console.log('Render: ', children)
return (
<div onClick={onClick}>
{children}: {value}
</div>
)
}
Counter = React.memo(Counter)
const App = () => {
const [count1, setCount1] = React.useState(0)
const [count2, setCount2] = React.useState(0)
const increaseCounter1 = useCallback(() => {
setCount1(count1 => count1 + 1)
}, [])
const increaseCounter2 = useCallback(() => {
setCount2(count2 => count2 + 1)
}, [])
console.log(increaseCounter1)
return (
<>
<Counter value={count1} onClick={increaseCounter1}>Counter 1</Counter>
<Counter value={count2} onClick={increaseCounter2}>Coutner 2</Counter>
</>
)
}
export default App
基本上90%的情况下,都应该用这个,这个是在render结束后,你的callback函数执行,但是不会block browser painting,算是某种异步的方式吧,但是class的componentDidMount 和componentDidUpdate是同步的,在render结束后就运行,useEffect在大部分场景下都比class的方式性能更好.
这个是用在处理DOM的时候,当你的useEffect里面的操作需要处理DOM,并且会改变页面的样式,就需要用这个,否则可能会出现出现闪屏问题, useLayoutEffect里面的callback函数会在DOM更新完成后立即执行,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制.
可能讲的不是很清楚,看个例子吧
import React, { useEffect, useLayoutEffect, useRef } from "react";
import TweenMax from "gsap/TweenMax";
import './index.less';
const Animate = () => {
const REl = useRef(null);
useEffect(() => {
/*下面这段代码的意思是当组件加载完成后,在0秒的时间内,将方块的横坐标位置移到600px的位置*/
TweenMax.to(REl.current, 0, {x: 600})
}, []);
return (
<div className='animate'>
<div ref={REl} className="square">square</div>
</div>
);
};
export default Animate;
可以清楚的看到有一个一闪而过的方块.
改成useLayoutEffect试试.可以看出,每次刷新,页面基本没变化
[转]为什么在 React 的 Render 中使用箭头函数和 bind 会造成问题 原文转自https://zhuanlan.zhihu.com/p/29266705
调试网站
codesandbox JSFiddle
三篇react hook不错的普及文
React Hooks 解析(下):进阶 React Hooks 解析(上):基础 你不知道的 useCallback 使用 useReducer 和 useCallback 解决 useEffect 依赖诚实与方法内置&外置问题 React Hooks 第一期:聊聊 useCallback
一份完整的useEffect指南 一份完整的useEffect指南(2) A Complete Guide to useEffect
未读待定 react-hooks中的一些懵逼点 未读待定 TypeScript 中使用React Hook