React 新特性学习

 

1 contextcss

2 contextTypereact

3 lazywebpack

4 suspenseweb

5 memo编程

6 hooks数组

7 effect hooksapp

===========ide

1 Context svg

  提供了一种方式,可以让数据在组件树中传递而没必要一级一级手动传递 (可是不要滥用,由于它会破坏组件的复用性)函数

 

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>
}
相关文章
相关标签/搜索