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>
}

React 新特性学习的更多相关文章

  1. react新特性 react hooks

    本文介绍的是react新特性react hooks,本文面向的是有一定react开发经验的小伙伴,如果你对react还不是很熟悉的话我建议你先学习react并多多联系. 首先我们都知道react有3种 ...

  2. Java8 新特性学习 Lambda表达式 和 Stream 用法案例

    Java8 新特性学习 Lambda表达式 和 Stream 用法案例 学习参考文章: https://www.cnblogs.com/coprince/p/8692972.html 1.使用lamb ...

  3. java8 新特性学习笔记

    Java8新特性 学习笔记 1主要内容 Lambda 表达式 函数式接口 方法引用与构造器引用 Stream API 接口中的默认方法与静态方法 新时间日期 API 其他新特性 2 简洁 速度更快 修 ...

  4. ES7/8新特性学习随笔

    随着每年EcmaScript都会为js带来一些新特性,带来更多美化的编程体验,今天就走进一下es2016/2017所带来的新特性 ES7新特性 includes() 指数操作符 ES8新特性 asyn ...

  5. java8新特性学习1

    java8增加了不少新特性,下面就一些常见的新特性进行学习... 1.接口中的方法 2.函数式接口 3.Lambda表达式 4.java8内置的四大核心函数式接口 5.方法引用和构造器引用 6.Str ...

  6. java8新特性学习:函数式接口

    本文概要 什么是函数式接口? 如何定义函数式接口? 常用的函数式接口 函数式接口语法注意事项 总结 1. 什么是函数式接口? 函数式接口其实本质上还是一个接口,但是它是一种特殊的接口:SAM类型的接口 ...

  7. java8新特性学习:stream与lambda

    Streams api 对 Stream 的使用就是实现一个 filter-map-reduce 过程,产生一个最终结果,或者导致一个副作用(side effect). 流的操作类型分为两种: Int ...

  8. 关于为什么使用React新特性Hook的一些实践与浅见

    前言 关于Hook的定义官方文档是这么说的: Hook 是 React 16.8 的新增特性.它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性. 简单来说,就是在 ...

  9. es6新特性学习

    本文用来记录一下es6的新特性,持续更新.... es6在前端目前还不能大面试使用,包括移动端兼容也不好.不过在node中已可以使用其中96%的特性.也可使用一些插件将es6转化为es5,比如babl ...

随机推荐

  1. 软件测试开发人员需要掌握的一些基本数据结构算法(php编写)

    一:冒泡排序算法    冒泡排序(Bubble Sort)算法是一种典型的交换排序算法,通过两两数据交换进行排序.如果有n个数,则要进行n-1趟比较,在第1趟比较中要进行n-1次两两比较,在第j趟比较 ...

  2. Detect cycle in a directed graph

    Question: Detect cycle in a directed graph Answer: Depth First Traversal can be used to detect cycle ...

  3. Redis 常用命令学四:集合类型命令

    1.增加和删除命令 127.0.0.1:6379> SADD st a (integer) 1 127.0.0.1:6379> SADD st r f g (integer) 3 127. ...

  4. Visual Studio中Debug与Release以及x86、x64、Any CPU的区别

    Visual Studio中Debug与Release的区别: 在Visual Studio中,编译模式有2种:Debug与Release.这也是默认的两种方式,在新建一个project的时候,就已经 ...

  5. chrome中显示DNS_PROBE_FINISHED_NO_INTERNET无法上网,但是IE可以上

    以管理员方式运行cmd,执行如下命令 ipconfig /release ipconfig /all ipconfig /flushdns ipconfig /renew netsh int ip s ...

  6. Ubuntu截图工具Flameshot

    今天来介绍一款Ubuntu下的截图工具,名叫Flameshot. 安装 Flameshot的安装很简单. 命令行安装 sudo apt-get install flameshot 一条命令搞定! 软件 ...

  7. Fabric中的节点类型

    在Fabric中,尽管所有对等节点/peer都是相同的,但它们可以根据网络的配置方式承担多个角色:(①②是主要的节点类型) ①提交节点: 通道中的每个对等节点都是一个提交节点.它们接收生成的交易区块, ...

  8. 【Scratch】编程?一节课就教会你!其实我们不用一个个学习如何使用代码。

    第199篇文章 老丁的课程 在很多教程里面,大家都喜欢把模块拿出来一个个讲述其功能. 这样做的好处是,可以把每个代码模块的功能讲的很清楚.但最最讨厌的问题也随之而来…… 举个例子,当你学习英语的时候, ...

  9. JVM学习笔记——类加载过程

    JVM学习笔记——类加载过程 类加载模型——双亲委派模型(Parents Delegation Model)也可称为“溯源委派加载模型” Java的类加载器是一个运行时核心基础设施模块,主要是启动之初 ...

  10. 基于【 Docker】三 || Docker的基本操作

    一.Docker常用命令 1.搜索镜像:docker search 镜像名称 2.下载镜像:docker pull 镜像名称:版本号 3.查看镜像:docker images 4.删除镜像:docke ...