React 新特性学习
1 context
2 contextType
3 lazy
4 suspense
5 memo
6 hooks
7 effect hooks
===========
1 Context
提供了一种方式,能够让数据在组件树中传递而不必一级一级手动传递 (但是不要滥用,因为它会破坏组件的复用性)
API: createContext(defaultValue)
示例1:基本用法
import React,{ Component,createContext } from 'react';//在这里导出context函数 import logo from './logo.svg'; import './App.css'; const BatteryContext = createContext();//在这里创建context //孙子组件 class Leaf extends Component { render(){ //在这里定义 consumer 消费者 return ( <BatteryContext.Consumer> { Battery => <h1>BatteryName:{Battery}</h1> } </BatteryContext.Consumer> ) } } //子组件 class Middle extends Component{ render(){ return <Leaf/>; } } //父组件 class App extends Component { render(){ //在这里定义 Provider context的提供者 return ( <BatteryContext.Provider value = {60}> <Middle/> </BatteryContext.Provider> ) } } export default App;
示例2: 动态改变其值:
import React,{ Component,createContext } from 'react';//在这里导出context函数 import logo from './logo.svg'; import './App.css'; const BatteryContext = createContext();//在这里创建context //孙子组件 class Leaf extends Component { render(){ //在这里定义 consumer 消费者 return ( <BatteryContext.Consumer> { Battery => <h1>BatteryName:{Battery}</h1> } </BatteryContext.Consumer> ) } } //子组件 class Middle extends Component{ render(){ return <Leaf/>; } } //父组件 class App extends Component { state = { battery:50 } render(){ const {battery} = state; //在这里定义 Provider context的提供者 return ( <BatteryContext.Provider value = {battery}> <button type="button" onclick={()=>{ this.setState({ battery: battery-1 }) }}/> <Middle/> </BatteryContext.Provider> ) } } export default App;
示例3:多个context嵌套
import React,{ Component,createContext } from 'react';//在这里导出context函数 import logo from './logo.svg'; import './App.css'; const BatteryContext = createContext();//在这里创建context const OnlineContext = createContext();//在这里创建context //孙子组件 class Leaf extends Component { render(){ //在这里定义 consumer 消费者 return ( <BatteryContext.Consumer> { battery => ( <OnlineContext.Consumer> { online => <h1>battery:{battery},online:{online}</h1> } </OnlineContext.Consumer> ) } </BatteryContext.Consumer> ) } } //子组件 class Middle extends Component{ render(){ return <Leaf/>; } } //父组件 class App extends Component { state = { battery:50, online:false, } render(){ const {battery,online} = state; //在这里定义 Provider context的提供者 return ( <BatteryContext.Provider value = {battery}> <OnlineContext.Provider value = {online}> <button type="button" onclick={()=>{ this.setState({ battery: battery-1 }) }}> </button> <button type="button" onclick={()=>{ this.setState({ online: !online }) }}/> </button> <Middle/> </OnlineContext.Provider> </BatteryContext.Provider> ) } } export default App;
示例4:使用static 化简
import React,{ Component,createContext } from 'react';//在这里导出context函数 import logo from './logo.svg'; import './App.css'; const BatteryContext = createContext();//在这里创建context const OnlineContext = createContext();//在这里创建context //孙子组件 class Leaf extends Component { static contextType = BatteryContext; render(){ const battery = this.context; return ( <h1>battery:{battery}</h1> ) } } //子组件 class Middle extends Component{ render(){ return <Leaf/>; } } //父组件 class App extends Component { state = { battery:50, online:false, } render(){ const {battery,online} = state; //在这里定义 Provider context的提供者 return ( <BatteryContext.Provider value = {battery}> <OnlineContext.Provider value = {online}> <button type="button" onclick={()=>{ this.setState({ battery: battery-1 }) }}> </button> <button type="button" onclick={()=>{ this.setState({ online: !online }) }}/> </button> <Middle/> </OnlineContext.Provider> </BatteryContext.Provider> ) } } export default App;
2 lazy 加载
懒加载组件:
// app.js import React,{ Component ,lazy ,Suspense} from 'react'; //lazy 的返回就是一个react组件 const About = lazy(()=> import('./About.jsx')); //父组件 必须用 Suspense 和lazy进行配合,fallback中加载一个实例 // 或者把 <div>Loading</div> 改为 <Loading/> 组件 class App extends Component { render(){ return ( <div> <Suspense fallback={<div>Loading</div>}> <About/> </Suspense> </div> ) } } export default App;
子组件:
//About.js import React,{ Component } from 'react'; //子组件 export default class About extends Component { render(){ return <div>About</div> } }
从 开发者工具中的 network 中可以看到:
如果想改变 trunk 的名字,将app父组件改为下面的方式:
// app.js import React,{ Component ,lazy ,Suspense} from 'react'; //lazy 的返回就是一个react组件 const About = lazy(()=> import(/* webpackChunkName:"about"*/'./About.jsx'));
则:
如果子组件加载失败,增加容错机制:
// app.js import React,{ Component ,lazy ,Suspense} from 'react'; //lazy 的返回就是一个react组件 const About = lazy(()=> import(/* webpackChunkName:"about"*/'./About.jsx')); // ErrorBoundary 错误边界 // componentDidCatch 生命周期函数 如果 render() 函数抛出错误,则会触发该函数。 // 错误在渲染阶段中被捕获,但在事件处理程序中不会被捕获 class App extends Component { state = { hasError:false }; componentDidCatch(){ this.setState({ hasError:true }) } render(){ if(this.state.hasError){ return <div>error</div> } return ( <div> <Suspense fallback={<div>Loading</div>}> <About/> </Suspense> </div> ) } } export default App;
3。memo
先看一个示例:父组件每次更新state中的 count,其子组件 Foo 都会执行;
// App.jsx import React,{ Component } from 'react'; //子组件 class Foo extends Component { render(){ console.log('foo render'); return null; } } //父组件 class App extends Component { state={ count:0, } render(){ return ( <div> <button onclick={()=>{ this.setState({ count:this.state.count++ }) }}></button> <Foo name="Milk"/> </div> ) } } export default App;
优化一:使用生命周期函数 shouldComponentUpdate:
子组件改为:
//子组件 class Foo extends Component { shouldComponentUpdate(nextProps,nextState){ if(nextProps.name === this.props.name){ return false; } return true; } render(){ console.log('foo render'); return null; } }
优化二: 使用 PureComponent ,子组件改为:
// App.jsx import React,{ Component,PureComponent } from 'react'; //子组件 class Foo extends PureComponent { render(){ console.log('foo render'); return null; } }
但是有局限性,只能对传入的属性做简单的对比,如果属性内部发生变化,则不会监听到,导致子组件不会改动:
// App.jsx import React,{ Component,PureComponent } from 'react'; //子组件 class Foo extends PureComponent { render(){ console.log('foo render'); return <div>{this.props.person.age}</div>; } } //父组件 class App extends Component { state={ count:0, person:{ age:1, } } render(){ const person = this.state.person; return ( <div> <button onclick={()=>{ person.age++ this.setState({ person }) }}></button> <Foo person={person}/> </div> ) } } export default App;
如上所示:改变了父组件中state的 person对象中的age属性,但是子组件是无法监听到的,只能监听到第一级的数据;
另一个局限:
父组件给子组件有个内联函数: <Foo person={person} cd={()=>{}}/> 的时候,每次改变父组件的state值,都会创建一个新的函数对象。子组件都会被重新渲染;
类似的 <Foo person={person} cd={this.callback.bind(this)}/> ,子组件也会被重新渲染。
改为下面的方法,即可以:
//父组件 class App extends Component { state={ count:0, person:{ age:1, } } callback=()=>{ } render(){ const person = this.state.person; return ( <div> <Foo person={person} cd={this.callback}/> </div> ) } } export default App;
将子组件改为无状态函数后,每次改变state 子组件也会改变:
// App.jsx import React,{ Component,PureComponent } from 'react'; //子组件 function Foo(props) { render(){ console.log('foo render'); return <div>{props.person.age}</div>; } } //父组件 class App extends Component { state={ count:0, person:{ age:1, } } callback=()=>{ } render(){ const person = this.state.person; return ( <div> <Foo person={person} cd={this.callback}/> </div> ) } } export default App;
但是这样的话,每次子组件都会被渲染,这时候 memo 出现了:它相当于 class 中的 PureComponent:
// App.jsx import React,{ Component,PureComponent ,memo } from 'react'; //子组件 const Foo = memo( function Foo(props) { render(){ console.log('foo render'); return <div>{props.person.age}</div>; } } ) //父组件 class App extends Component { state={ count:0, person:{ age:1, } } callback=()=>{ } render(){ const person = this.state.person; return ( <div> <Foo person={person} cd={this.callback}/> </div> ) } } export default App;
这样,拆分的组件传入的属性越简单,越容易写成上述方式;
6 hooks
类组件不足:
1 状态逻辑复用难:缺少复用机制,渲染属性和高阶组件导致层级冗余;
2 趋向于复杂难以维护: 生命周期函数混杂不相干逻辑,相干逻辑分散在不同生命周期
3 this 指向困扰:内联函数过度创建新句柄,累成员函数不能保证this;
(注意:点击组件内的按钮,state中的count发生变化,组件将渲染,但设置默认state只在第一次时渲染)
// react 分为 聪明组件和傻瓜组件 按我的理解 这个应该用在傻瓜组件中 // 父组件中 state的变化 导致子组件中 props的变化 导致子组件的重新渲染 function App(){ const [count,setCount] = useState(0); const [name,setName] = useState('like'); return ( <button type="button" onClick = {()=>{setCount(count++)}} > {count} {name} </button> ) }
问题一:useState设置的默认值,如何知道对应的给state?
答: useState本身只是单纯的设置一个值,然后是结构赋值功能,赋给了对应的state;
问题二:每个组件都有useState,它是如何保证只赋值给当前组件中的count,而不是其他组件的count呢?
答: 利用了js的单线程原理,当前只能赋值给当前作用域下的组件。
问题三: 如果一个组件有多个useState, 每次渲染该组件的时候,是如何返给定义的state 呢?
答:useState是根据第一次渲染执行组件的时候,定义的state顺序赋值的,所以不能改变赋值的顺序。例如下面的示例:
let id = 0; function App(){ let name,setName; let count,setCount; id++; if(id & 1){//如果id是奇数 [count,setCount] = useState(0); [name,setName] = useState('like'); }else{ [name,setName] = useState('like'); [count,setCount] = useState(0); } return ( <button type="button" onClick = {()=>{setCount(count++)}} > {count} {name} </button> ) }
下面的形式也是不行的:
let id = 0; function App(){ const [count,setCount] = useState(0); const [name,setName] = useState('like'); id++; if(id>1){ useState('dd'); //从第二次渲染之后 增加的的设置 } return ( <button type="button" onClick = {()=>{setCount(count++)}} > {count} {name} </button> ) }
下面的形式也不行:
let id = 0; function App(){ const [count,setCount] = useState(0); const [name,setName] = useState('like'); id++; if(id===1){ useState('dd'); //只在第一次渲染时 增加的的设置 } return ( <button type="button" onClick = {()=>{setCount(count++)}} > {count} {name} </button> ) }
比如说 state的默认值是基于 props 的:注意:点击组件内的按钮,state中的count发生变化,组件将渲染,但设置默认state只在第一次时渲染
function App(){ const [count,setCount] = useState(()=>{ return props.defaultCount || 0; //只会执行一次 }); const [name,setName] = useState('like'); return ( <button type="button" onClick = {()=>{setCount(count++)}} > {count} {name} </button> ) }
7 effect hooks 副作用时机
原来的生命周期:
Mount之后: componentDidMount
update 之后 componentDidUpdate
Unmount 之前 componentWillUnmount
用 useEffect 函数来替换;
userEffect函数是在 render之后调用的 ,其功能相当于 componentDidMount/和componentDidUpdate,并且该函数有callback函数,其功能是清除上一次副作用 遗留下来的状态 相当于componentWillUnmount
示例1: 点击按钮 文本中和页面title均发生变化,使用原来的生命周期开发:
export default class App extends Component { state = { count:0 } componentDidMount(){ doucument.title = this.state.count; } componentDidUpdate(){ doucument.title = this.state.count; } render(){ const { count } = this.state; return ( <button type="button" onClick={()=>{ this.setState({ count:count++ }) }}> {count} </button> ) } }
可见,为了实现初始化时和数据更新时,title发生变化,同样的交互代码要在两个生命周期中执行两次,类似的 再加上 监听函数,需要在卸载生命周期中 去掉卸载函数:
export default class App extends Component { state = { count:0, size:{ with:doucument.doucumentElement.clientWidth, height:doucument.doucumentElement.clientHeight } } componentDidMount(){ doucument.title = this.state.count; window.addEvevntListener('resize',this.onResize,false); } componentwillUnMount(){ window.removeEventListner('resize',this.onResize,false); } onResize = ()=>{ this.setState({ size:{ with:doucument.doucumentElement.clientWidth, height:doucument.doucumentElement.clientHeight } }) } componentDidUpdate(){ doucument.title = this.state.count; } render(){ const { count,size } = this.state; return ( <button type="button" onClick={()=>{ this.setState({ count:count++ }) }}> {count} size:{size.width}X{size.height} </button> ) } }
现在使用 effect hooks:
//1 提高了代码复用(合并多个生命周期成了一个函数)
//2 优化了关注点分离,即不同的事件放在了不同的 useEffect 函数中
function App(){ //定义初始化数据 const [count,setCount] = useState(0); const [size,setSize] = useState({ with:doucument.doucumentElement.clientWidth, height:doucument.doucumentElement.clientHeight }); //常规函数 const onResize = ()=>{ setState({ with:doucument.doucumentElement.clientWidth, height:doucument.doucumentElement.clientHeight }) } //1 提高了代码复用(合并多个生命周期成了一个函数) //2 优化了关注点分离,即不同的事件放在了不同的 useEffect 函数中 //使用副作用在某些生命周期中执行数据的操作 useEffect(()=>{ doucument.title = count; }) useEffect(()=>{ window.addEvevntListener('resize',onResize,false); return ()=>{ //默认是组件重渲染和组件卸载的时候执行 window.addEvevntListener('resize',onResize,false); } },[]); //上面useEffect函数的空数组的参数,其作用是用于比对。决定该 useEffect 是否执行 // 如果第二个参数不写,则每次都会执行这个 useEffect ,如果为空数组,则只执行一次 // 如果数组中写了数据,则比对每一个数据,只有数组中的每一项都不变的情况下,才会再次执行; // 如下面,变化size 不会触发下面useEffect的函数执行 useEffect(()=>{ console.log(count); },[count]) return ( <button type="button" onClick = {()=>{setCount(count++)}}> {count} size:{size.width}X{size:height} </button> ) } export default App;
8 hooks 环境下的的context
由前面 context知识可以知道 ContextType 只能存在于 Class中,则hook是的无状态函数咋整?
下面的示例给出了使用context的三个方法:
import React,{ Component, useState, createContext, useContext} from 'react'; const CountContext = createContext();//在这理定义context的外层组件 //子组件(最基础的写法) class Foo extends Component{ render(){ return ( <CountContext.Consumer> { count => <h1>{count}</h1> } </CountContext.Consumer> ) } } //子组件(优化的写法)适用于类组件 class Bar extends Component{ static contextType = CountContext; render(){ const { count} = this.state; return ( <h1>{count}</h1> ) } } //hooks中使用 context 可以获取多个 context function Counter(){ const count = useContext(CountContext); return ( <h1>{count}</h1> ) } //父组件 export default class App extends Component { const [ count,setCount] = useState(0); render(){ return ( <div> <button type="button" onClick={()=>{setCount(count+1)}} > click({count})</button> <CountContext.Provider value={count}> <Foo/> <Counter/> </CountContext.Provider> </div> ) } }
9 hooks中的 useMemo 函数
不同点:
- useMemo函数是在渲染过程中执行,同比 useEffect是在渲染后执行;
- useMemo函数有返回值,同比 useEffect 没有返回值;
相同点:
useMemo 函数和 useEffect 函数均有第二个参数,决定是否执行该函数。
示例:
import React,{ Component, useState, useMemo} from 'react'; function Counter(props){ return ( <h1>{props.count}</h1> ) } function App(){ const [count,setCount] = useState(0); const double = useMemo(()=>{ return count*2; },[count === 3]) return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),double:({double}) </button> <Counter count={count}/> </div> ) } export default App;
如上所示,当 count==3的时候,useMemo中数组的值由 false变为true, double 发生变化
当 count ==4 的时候, useMemo 中数组的值。由true 变为 false,double 再次发生变化;
import React,{ Component, useState, useMemo} from 'react'; function Counter(props){ return ( <h1>{props.count}</h1> ) } function App(){ const [count,setCount] = useState(0); const double = useMemo(()=>{ return count*2; },[count === 3]); // 还可以依赖 memo const half = useMemo(()=>{ return double/4; },[double]); return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),double:({double}) </button> <Counter count={count}/> </div> ) } export default App;
10 hooks中的 callback 函数
首先看一下memo函数,用memo包裹Counter函数,只有count发生变化的时候,才执行Count函数;
import React,{ Component, useState, useMemo, memo} from 'react'; const Counter = memo(function Counter(props){ cosole.log('count 发生变化的时候才执行'); return ( <h1>{props.count}</h1> ) }) function App(){ const [count,setCount] = useState(0); const double = useMemo(()=>{ return count*2; },[count === 3]); return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),double:({double}) </button> <Counter count={double}/> </div> ) }
这时给 子组件 Counte 增加 回调函数 onclick
import React,{ Component, useState, useMemo, memo} from 'react'; const Counter = memo(function Counter(props){ cosole.log('count 发生变化的时候才执行'); return ( <h1 onClick={props.onClick}>{props.count}</h1> //这里 ) }) function App(){ const [count,setCount] = useState(0); const double = useMemo(()=>{ return count*2; },[count === 3]); const onClick = ()=>{ console.log('click me'); //父组件中定义回调函数 } return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),double:({double}) </button> <Counter count={double} onClick={onClick}/> //监听的函数 </div> ) } export default App;
由于回调函数 onclick的存在,每次父组件中app的变化,都 会导致子组件发生渲染;所以可以在父组件中使用 memo
function App(){ const [count,setCount] = useState(0); const double = useMemo(()=>{ return count*2; },[count === 3]); const onClick = useMemo(()=>{ return ()=>{ console.log('click me'); } },[]); //改变了这里 return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),double:({double}) </button> <Counter count={double} onClick={onClick}/> </div> ) }
然后使用 useCallback 化简:
function App(){ const [count,setCount] = useState(0); const double = useMemo(()=>{ return count*2; },[count === 3]); const onClick = useCallback(()=>{ console.log('click me'); },[]); //改变了这里 // useMemo(()=>fn); // useCallback(fn); useCallback 相当于是简化写法 return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),double:({double}) </button> <Counter count={double} onClick={onClick}/> </div> ) }
这样,就不会因为父组件中的 回调函数 onClick的变化导致子组件发生变化:
1 子组件中使用 memo函数可以避免重复渲染,而是根据传入的props发生变化时才渲染;
2 父组件中使用 useMemo函数可以避免因为 回调函数的存在,导致子组件的渲染;
11 hooks中的 useRef
- 获取子组件或者DOM节点的句柄
- 渲染周期之间共享数据的存储
示例1:
import React,{ Component, PureComponent,useRef} from 'react';//这里引入 useRef组件 class Counter extends PureComponent { speak(){ console.log('speak'); } render(){ const { props } = this; return ( <h1 onClick={props.onClick}>{props.count}</h1>
) } } function App(){ const [count,setCount] = useState(0); const counterRef = useRef();//创建一个ref,在组件中使用该counrerRef const onClick = useCallback(()=>{ counterRef.current.speak();//执行子组件中的speak函数,current属性 获取最终的值 },[counterRef]); return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}) </button> <Counter ref={counterRef} count={double} onClick={onClick}/> </div> ) } export default App;
示例2: 假设组件中定义一个变量,每秒加1,要求大于10之后不再增加;
function App(){ const [count,setCount] = useState(0); const counterRef = useRef(); let it; //因为更新state,会导致app组件重新渲染,it会重新初始化,而state只在第一次初始化,但是也不便于将it //放在state中,毕竟它没有用于渲染组件 useEffect(()=>{ it = setInterval(()=>{ setCount(count=>count+1) },1000) },[]); useEffect(()=>{ if(count >= 10){ clearInterval(it); } }) const onClick = useCallback(()=>{ counterRef.current.speak(); },[counterRef]); return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}) </button> <Counter ref={counterRef} count={double} onClick={onClick}/> </div> ) }
使用ref,将it改为类似于类的结构成员变量:
import React,{ Component, PureComponent,useEffect,useRef} from 'react';//这里引入 useRef组件 class Counter extends PureComponent { speak(){ console.log('speak'); } render(){ const { props } = this; return ( <h1 onClick={props.onClick}>{props.count}</h1> } } function App(){ const [count,setCount] = useState(0); const counterRef = useRef(); let it = useRef();//改变了这里 useEffect(()=>{ it.current = setInterval(()=>{ //改变了这里 it.current setCount(count=>count+1) },1000) },[]); useEffect(()=>{ if(count >= 10){ clearInterval(it.current);//改变了这里 it.current } }) const onClick = useCallback(()=>{ counterRef.current.speak(); },[counterRef]); return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}) </button> <Counter ref={counterRef} count={double} onClick={onClick}/> </div> ) } export default App;
最后:自定义hooks
funciton useCount(defaultCount){ const [count,setCount] = useState(defaultCount); const it = useRef(); useEffect(()=>{ it.current = setInterval(()=>{ setCount(count=>count +1 ); },1000) },[]); useEffect(()=>{ if(count >= 10){ clearInterval(it.current); } }); return [count,setCount] }
示例2 :
import React,{ Component, PureComponent,useEffect,useRef} from 'react';//这里引入 useRef组件 function useCounter(count) { const size = useSize();//共用 useSize函数1 return ( <h1>{count},{size.width},{size.height}</h1> ) } function useSize(){ const [size,setSize] = useSize({ width: document.documentElement.clientWidth, height: document.documentElement.clientHeigth }) const onResize = useCallback(()=>{ setSize({ width: document.documentElement.clientWidth, height: document.documentElement.clientHeigth }) },[]); useEffect(()=>{ window.addEventListener('resize',onResize,false); return ()=>{ window.removeEventListener('resize',onResize,false); } },[]) } funciton useCount(defaultCount){ const [count,setCount] = useState(defaultCount); const it = useRef(); useEffect(()=>{ it.current = setInterval(()=>{ setCount(count=>count +1 ); },1000) },[]); useEffect(()=>{ if(count >= 10){ clearInterval(it.current); } }); return [count,setCount] } function App(){ const [count,setCount] = useCount(0); //这里也是自定义的hooks组件 const Counter = useCounter(count); // 这里调用的是自定义的hooks函数useCounter const size = useSize(); //共用 useSize函数2 return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),<h1>{count},{size.width},{size.height}</h1> </button> {Counter} //这里调用的 上面 useCounter(count) </div> ) } export default App;
最后注意:
一般hooks 都是由 use 为前缀的,一定要遵循:
1. 把hook 放在最顶层,不要放在条件语句中,因为它依赖顺序;
2. 仅在组件和自定义hooks组件中调用,不要在其他普通函数中调用,因为普通函数说不清在哪里会被调用,导致hooks的顺序变化,例如
function useLogin (){ //自定义hooks,在其他地方调用也会是在顶层 有顺序的 const [login.setLogin] = useState(); useEffect(()=>{ }) } function fetchNews(){//而普通函数,说不清在哪里被调用,有肯能导致顺序不一样 const [pageNo,setPageNo] = useState(); }
===============分割线
Hooks 常见问题:
对传统react 编程的影响
1 生命周期函数如何映射到hooks
function App(){ useEffect(()=>{ //componentDidMount return ()=>{ //componentWillUnmount } },[]);//第二个参数为空数组,则只执行一次。挂载时执行一次,卸载时执行一次。 let renderCounter = useRef(0); renderCounter.current++; useEffect(()=>{ if(renderCounter >1){ // componentDidUpdate } }) //没有第二个参数,则每次都执行 }
2 类实例成员变量如何映射到hooks?
答:使用 useRef
3 Hooks 中如何获取历史props和state
function Counter(){ const [count,setCount] = useState(0); const preCountRef = useRef();//useRef不会受组件重新渲染的影响,保留上一次的值,所以定义了ref的值 preCountRef useEffect(()=>{ preCountRef.current = count; //没有第二个参数,表示每次都执行。则update时将count赋值给ref }) const prevCount = prevCountRef.current; return <h1>now:{count},before:{prevCount}</h1> }
4 如何强制更新一个Hooks组件?
思路:设置一个没有参与渲染的data,然后改变它的值:
function Counter(){ const [count,setCount] = useState(0); const [updater,setUpdater] = useState(0); function forceUpdate(){ setUpdater(updater => updater+1);//组件没有用到这个data,强制执行该函数,则更新渲染组件 } const preCountRef = useRef(); useEffect(()=>{ preCountRef.current = count; }) const prevCount = prevCountRef.current; return <h1>now:{count},before:{prevCount}</h1> }