We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
使用 Context ,首先顶层先声明 Provier 组件,并声明 value 属性,接着在后代组件中声明 Consumer 组件,这个 Consumer 子组件,只能是唯一的一个函数,函数参数即是 Context 的负载。如果有多个 Context ,Provider 和 Consumer 任意的顺序嵌套即可。
Provier
value
Consumer
Context
Provider
此外我们还可以针对任意一个 Context 使用 contextType 来简化对这个 Context 负载的获取。但在一个组件中,即使消费多个 Context,contextType 也只能指向其中一个
contextType
在 Hooks 环境中,依旧可以使用 Consumer,但是 ContextType 作为类静态成员肯定是用不了。Hooks 提供了 useContext,不但解决了 Consumer 难用的问题同时也解决了 contextType 只能使用一个 context 的问题。
ContextType
useContext
context
来个使用类形式的例子:
class Foo extends Component { render() { return ( <CountContext.Consumer> { count => <h1>{count}</h1> } </CountContext.Consumer> ) } } function App (props) { const [count, setCount] = useState(0); return ( <div> <button type="button" onClick={() => {setCount(count + 1) }} > Click({count}) </button> <CountContext.Provider value={count}> <Counter /> </CountContext.Provider> </div> ) }
以上就不说解释了,第一篇已经讲过了,接着将 Foo 改成用 contextType 的形式:
Foo
class Foo extends Component { static contextType = CountContext; render() { const count = this.context return ( <h1>{count}</h1> ) } }
接着使用 useContext 形式:
function Foo () { const count = useContext(CountContext) return ( <h1>{count}</h1> ) }
useContext 接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <CountContext.Provider> 的 value prop 决定。
<CountContext.Provider>
当组件上层最近的 <CountContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 CountContext provider 的 context value 值。
别忘记 useContext 的参数必须是 context 对象本身:
meno 用来优化函数组件重渲染的行为,当传入属性值都不变的情况下,就不会触发组件的重渲染,否则就会触发组件重渲染。
meno针对的是一个组件的渲染是否重复执行,而 useMemo 定义的是一个函数逻辑是否重复执行。
meno
useMemo
来个粟子:
function Foo (props) { return ( <h1>{props.count}</h1> ) } function App (props) { const [count, setCount] = useState(0); const double = useMemo(() => { return count * 2 }, [count]) return ( <div> <button type="button" onClick={() => {setCount(count + 1) }} > Click({count}) double: ({double}) </button> <Foo count={count}/> </div> ) }
运行结果:
如上所示,useMemo 语法与 useEffect 是一致的。第一个参数是需要执行的逻辑函数,第二个参数是这个逻辑依赖输入变量组成的数组,如果不传第二个参数,这 useMemo 的逻辑每次就会运行,useMemo 本身的意义就不存在了,所以需要传入参数。所以传入空数组就只会运行一次,策略与 useEffect 是一样的,但有一点比较大的差异就是调用时机,useEffect 执行的是副作用,所以一定是渲染之后才执行,但 useMemo 是需要返回值的,而返回值可以直接参与渲染,因此 useMemo 是在渲染期间完成的。
useEffect
接下来改造一下 useMemo,让它依赖 count 如下:
count
const double = useMemo(() => { return count * 2 }, [count])
接着只有当 count 变化时,useMemo 才会执行。
再次修改 useMemo, 如下:
const double = useMemo(() => { return count * 2 }, [count === 3])
现在能断定,count 在等于 3 之前,由于这个条件一直保存 false 不变,double 不会重新计算,所以一直是 0,当 count 等于 3,double 重新计算为 6,当 count 大于 3,double 在重新计算,变成 8,然后就一直保存 8 不变。
false
double
记住,传入的 useMemo 的函数会在渲染期间执行,请不要在这个函数内部执行与渲染无关的听任,诸如副作用这类操作属于 useEffect 的适用范畴,而不是 useMemo。
你可以把 useMemo 作为性能优化的手段,但不要把它当成语义上的保证。
接下先看一下使用 memo 优化子组件的例子。
memo
const Foo = memo (function Foo (props) { console.log('Counter render') return ( <h1>{props.count}</h1> ) }) function App (props) { const [count, setCount] = useState(0); const double = useMemo(() => { return count * 2 }, [count === 3]) return ( <div style={{padding:'100px'}}> <button type="button" onClick={() => {setCount(count + 1) }} > Click({count}) double: ({double}) </button> <Foo count={double}/> </div> ) }
使用 memo 包裹 Foo 组件,这样只有当 double 变化时,Foo 组件才会重新渲染,执行里面的 log,运行结果如下:
现在在给 Foo 中的 h1 添加一个 click 事件:
h1
click
const Foo = memo (function Foo (props) { console.log('Counter render') return ( <h1 onClick={props.onClick}>{props.count}</h1> ) })
然后在 App 组件中声明 onClick 并传给 Foo 组件:
function App (props) { ... const onClick = () => { console.log('Click') } return ( <div style={{padding:'100px'}}> ... <Foo count={double} onClick={onClick}/> </div> ) }
看下运行效果:
可以看出,每次点击,不管 double 是否有变化, Foo 组件都会被渲染。那就说明每次 App 重新渲染之后, onClick 句柄的变化,导致 Foo 也被连带重新渲染了。count 经常变化可以理解,但是 onClick 就不应该经常变化了,毕竟只是一个函数而已,所以我们要想办法让 onClick 句柄不变化。
onClick
想想我们上面讲的 useMemo,可以这样来优化 onClick:
const onClick = useMemo(() => { return () => { console.log('Click') } }, [])
由于我们传给 useMemo 的第二个参数是一个空数组,那么整个逻辑就只会运行一次,理论上我们返回的 onClick 就只有一个句柄。
运行效果:
现在我们把 useCallback 来实现上页 useMemo 的逻辑。
useCallback
const onClick = useCallback(() => { console.log('Click') },[])
如果 useMemo 返回的是一个函数,那么可以直接使用 useCallback 来省略顶层的函数。
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)
大家可能有一个疑问,useCallback 这几行代码明明每次组件渲染都会创建新的函数,它怎么就优化性能了呢。
注意,大家不要误会,使用 useCallback 确实不能阻止创建新的函数,但这个函数不一定会被返回,也就是说这个新创建的函数可能会被抛弃。useCallback解决的是解决的传入子组件的函数参数过多变化,导致子组件过多渲染的问题,这里需要理解好。
上述我们第二个参数传入的空数组,在实际业务并没有这么简单,至少也要更新一下状态。举个粟子:
function App (props) { ... const [clickCount, setClickCount] = useState(0); const onClick = useCallback(() => { console.log('Click') setClickCount(clickCount + 1) },[clickCount, setClickCount]) ... }
在 APP 组件中在声明一个 useState,然后在 onClick 中调用 setClickCount,此时 onClick 依赖 clickCount,setClickCount。
useState
setClickCount
clickCount
其实这里的 setClickCount 是不需要写的,因为 React 能保证 setState 每次返回的都是同个句柄。不信,可以看下官方文档 :
setState
这里的场景,除了直接使用 setClickCount + 1 赋值以外, 还有一种方式甚至连 clickCount都不用依赖。setState 除了传入对应的 state 最新值以外,还可以传入一个函数,函数的参数即这个 state 的当前值,返回就是要更新的值:
setClickCount + 1
state
const onClick = useCallback(() => { console.log('Click') setClickCount((clickCount) => clickCount + 1) },[])
和 memo 根据属性来决定是否重新渲染组件一样,useMemo 可以根据指定的依赖不决定一段函数逻辑是否重新执行,从而优化性能。
如果 useMemo 的返回值是函数的话,那么就可以简写成 useCallback 的方式,只是简写而已,实际并没有区别。
需要特别注意的是,当依赖变化时,我们能断定 useMemo 一定重新执行。但是,即使依赖不变化我们不能假定它就一定不会重新执行,也就是说,它可以会执行,就是考虑内在优化结果。
我们可以把 useMemo, useCallback 当做一个锦上添花优化手段,不可以过度依赖它是否重新渲染,因为 React 目前没有打包票说一定执行或者一定不执行。
干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。
https://github.com/qq449245884/xiaozhi
我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!
关注公众号,后台回复福利,即可看到福利,你懂的。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
使用 Context Hooks
使用 Context ,首先顶层先声明
Provier
组件,并声明value
属性,接着在后代组件中声明Consumer
组件,这个Consumer
子组件,只能是唯一的一个函数,函数参数即是Context
的负载。如果有多个Context
,Provider
和Consumer
任意的顺序嵌套即可。此外我们还可以针对任意一个
Context
使用contextType
来简化对这个Context
负载的获取。但在一个组件中,即使消费多个Context
,contextType
也只能指向其中一个在 Hooks 环境中,依旧可以使用
Consumer
,但是ContextType
作为类静态成员肯定是用不了。Hooks 提供了useContext
,不但解决了 Consumer 难用的问题同时也解决了contextType
只能使用一个context
的问题。来个使用类形式的例子:
以上就不说解释了,第一篇已经讲过了,接着将
Foo
改成用contextType
的形式:接着使用
useContext
形式:useContext
接收一个context
对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的<CountContext.Provider>
的 value prop 决定。当组件上层最近的 <CountContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 CountContext provider 的 context value 值。
别忘记
useContext
的参数必须是context
对象本身:调用了
useContext
的组件总会在context
值变化时重新渲染。如果重渲染组件的开销较大,你可以 通过使用 memoization 来优化。使用 Memo Hooks
meno 用来优化函数组件重渲染的行为,当传入属性值都不变的情况下,就不会触发组件的重渲染,否则就会触发组件重渲染。
useMemo 与 memo
meno
针对的是一个组件的渲染是否重复执行,而useMemo
定义的是一个函数逻辑是否重复执行。来个粟子:
运行结果:
如上所示,
useMemo
语法与useEffect
是一致的。第一个参数是需要执行的逻辑函数,第二个参数是这个逻辑依赖输入变量组成的数组,如果不传第二个参数,这useMemo
的逻辑每次就会运行,useMemo
本身的意义就不存在了,所以需要传入参数。所以传入空数组就只会运行一次,策略与useEffect
是一样的,但有一点比较大的差异就是调用时机,useEffect
执行的是副作用,所以一定是渲染之后才执行,但useMemo
是需要返回值的,而返回值可以直接参与渲染,因此useMemo
是在渲染期间完成的。接下来改造一下
useMemo
,让它依赖count
如下:接着只有当 count 变化时,
useMemo
才会执行。再次修改 useMemo, 如下:
现在能断定,
count
在等于 3 之前,由于这个条件一直保存false
不变,double 不会重新计算,所以一直是 0,当count
等于 3,double
重新计算为 6,当count
大于 3,double
在重新计算,变成 8,然后就一直保存 8 不变。记住,传入的
useMemo
的函数会在渲染期间执行,请不要在这个函数内部执行与渲染无关的听任,诸如副作用这类操作属于useEffect
的适用范畴,而不是useMemo
。你可以把 useMemo 作为性能优化的手段,但不要把它当成语义上的保证。
使用 useCallback Hooks
接下先看一下使用
memo
优化子组件的例子。使用
memo
包裹Foo
组件,这样只有当double
变化时,Foo
组件才会重新渲染,执行里面的 log,运行结果如下:现在在给
Foo
中的h1
添加一个click
事件:然后在 App 组件中声明 onClick 并传给
Foo
组件:看下运行效果:
可以看出,每次点击,不管
double
是否有变化,Foo
组件都会被渲染。那就说明每次 App 重新渲染之后,onClick
句柄的变化,导致Foo
也被连带重新渲染了。count
经常变化可以理解,但是onClick
就不应该经常变化了,毕竟只是一个函数而已,所以我们要想办法让onClick
句柄不变化。想想我们上面讲的
useMemo
,可以这样来优化onClick
:由于我们传给
useMemo
的第二个参数是一个空数组,那么整个逻辑就只会运行一次,理论上我们返回的onClick
就只有一个句柄。运行效果:
现在我们把
useCallback
来实现上页useMemo
的逻辑。如果
useMemo
返回的是一个函数,那么可以直接使用useCallback
来省略顶层的函数。大家可能有一个疑问,
useCallback
这几行代码明明每次组件渲染都会创建新的函数,它怎么就优化性能了呢。注意,大家不要误会,使用
useCallback
确实不能阻止创建新的函数,但这个函数不一定会被返回,也就是说这个新创建的函数可能会被抛弃。useCallback
解决的是解决的传入子组件的函数参数过多变化,导致子组件过多渲染的问题,这里需要理解好。上述我们第二个参数传入的空数组,在实际业务并没有这么简单,至少也要更新一下状态。举个粟子:
在 APP 组件中在声明一个
useState
,然后在onClick
中调用setClickCount
,此时 onClick 依赖clickCount
,setClickCount
。其实这里的
setClickCount
是不需要写的,因为 React 能保证setState
每次返回的都是同个句柄。不信,可以看下官方文档 :这里的场景,除了直接使用
setClickCount + 1
赋值以外, 还有一种方式甚至连clickCount
都不用依赖。setState
除了传入对应的state
最新值以外,还可以传入一个函数,函数的参数即这个state
的当前值,返回就是要更新的值:小结
和
memo
根据属性来决定是否重新渲染组件一样,useMemo
可以根据指定的依赖不决定一段函数逻辑是否重新执行,从而优化性能。如果
useMemo
的返回值是函数的话,那么就可以简写成useCallback
的方式,只是简写而已,实际并没有区别。需要特别注意的是,当依赖变化时,我们能断定
useMemo
一定重新执行。但是,即使依赖不变化我们不能假定它就一定不会重新执行,也就是说,它可以会执行,就是考虑内在优化结果。我们可以把
useMemo
,useCallback
当做一个锦上添花优化手段,不可以过度依赖它是否重新渲染,因为 React 目前没有打包票说一定执行或者一定不执行。交流
干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。
我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!
关注公众号,后台回复福利,即可看到福利,你懂的。
The text was updated successfully, but these errors were encountered: