React Hooks入门

363 阅读5分钟

1.前言

一边学习,一边记笔记,如若有错误望指出,及时改正。
觉得文章可以,那就点个赞吧~
Hooks是react的一个新特性,是在react16.8以上的版本才能使用,

2.Hooks入门

写一个需求:点击按钮,让数字加1,效果图如下:

首先用基本的react语法写:
example.js

import React, { Component } from 'react';
class example extends Component {
    constructor(props) {
        super(props);
        this.state = { 
            temp:0
         };
    }
    onclick =()=>{
        this.setState({
            temp:this.state.temp+1
        })
    }
    render() {
        return (
            <div>
                <div>
                    clickResult is {this.state.temp}
                </div>
                <button onClick={this.onclick} style={{marginTop:"20px",marginBottom:"20px"}}>click</button>
            </div>
        );
    }
}
export default example;

现在使用hooks编写:
不利用继承component来创建组件,利用function来创建,利用useState来使用及更新状态:

import React, { useState } from 'react';
function Example (){
    const [temp, setCount] = useState(0);
    return (
        <div>
            <div>
                clickResult is {temp}
            </div>
            <button onClick={()=>{setCount(temp+1)}} style={{marginTop:"20px",marginBottom:"20px"}}>click this</button>
        </div>
    );

export default Example;

3.useState

1)如何声明

const [temp,setCount] = useState(0);//在此左边利用数组解构方式,等同于如下:
let _useState = useState(0);
let temp = _useState[0];
let setCount = _useState[1];

2)如何读取

useState这个函数接收的参数是状态的初始值(Initial state),它返回一个数组,这个数组的第0位是当前的状态值,第1位是可以改变状态值的方法函数。

就是利用{xxx}读取

3)如何改变

在上述例子中,利用setCount函数进行改变:

onClick={()=>{setCount(temp+1)}}

setCount这个函数接收的参数是修改过的新状态值。接下来的事情就交给React,他会重新渲染组件。React自动帮助我们记忆了组件的上一次状态值.

4)如何多状态声明

const [ age , setAge ] = useState(18)
const [ sex , setSex ] = useState('男')
const [ work , setWork ] = useState('前端程序员')
return (
    <div>
        <p>JSPang 今年:{age}岁</p>
        <p>性别:{sex}</p>
        <p>工作是:{work}</p>
        
    </div>
)

需要注意的是,useState不能在if...else...这样的条件语句中进行调用,必须要按照相同的顺序进行渲染。也就是React Hooks不能出现在条件判断语句中,因为它必须有完全一样的渲染顺序,不允许:

const [ age , setAge ] = useState(18)
if(showSex){
    const [ sex , setSex ] = useState('男')
    showSex=false
}

const [ work , setWork ] = useState('前端程序员')

这样会报错:

React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render 

4.useEffect

useEffect用于替换class创建组件时的生命周期:

利用useEffect实现componentDidmount和componentDidUpdate

先上效果图:

首先还是传统的class创建组件:

import React, { Component } from 'react';

class Example1 extends Component {
    constructor(props) {
        super(props);
        this.state = { temp:0 }
    }
    
    //React组件调用结束时调用
    componentDidMount(){
        console.log(`ComponentDidMount=>You clicked ${this.state.temp} times`)
    }
    //组件状态更改调用
    componentDidUpdate(){
        console.log(`componentDidUpdate=>You clicked ${this.state.temp} times`)
    }

    render() { 
        return (
            <div>
                <p>You clicked {this.state.temp} times</p>
                <button onClick={this.addCount.bind(this)}>Chlick me</button>
            </div>
        );
    }
    addCount(){
        this.setState({temp:this.state.temp+1})
    }
}

export default Example1;

再使用useEffect

import React, { useState,useEffect } from 'react';

function Example1 () {
    const [temp, setCount] = useState(0);
    useEffect(()=>{
        console.log(`useEffect=>You clicked ${temp} times`);
    })
    return (
        <div>
            <div>
                clickResult is {temp}
            </div>
            <button onClick={()=>{setCount(temp+1)}} style={{marginTop:"20px",marginBottom:"20px"}}>click this</button>
        </div>
    );
}
export default Example1;

运行可见,useEffect可以替代传统意义上的componentDidMount和componentDidUpdate,但是需要注意的是:
①:一个useEffect就可以代替传统的两个函数(didMount和DidUpdate); ②:useEffect是异步的,定义的函数的执行不会阻碍浏览器视图的更新,若想实时计算页面大小,就没办法同步实现,

利用useEffect实现componentWillUnMount函数

实现componentWillMount功能就是实现卸载功能,先安装路由,实现组件间跳转的组件销毁过程。

1)安装路由
npm install --save react-router-dom
2)搭建最基本的页面布局
import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';

function Interface() {
    return <h2>接口测试</h2>
}
function Group() {
    return <h2>组件测试</h2>
}
function Example2() {
    const [temp, setCount] = useState(0);
    return (
        <div>
            <div>
                clickResult is {temp}
            </div>
            <button onClick={() => { setCount(temp + 1) }} style={{ marginTop: "20px", marginBottom: "20px" }}>click this</button>
            <Router>
                <ul>
                    <li><Link to="/">测试中心</Link></li>
                    <li><Link to="/group">组件中心</Link></li>
                </ul>
                <Route path="/" exact component={Interface} />
                <Route path="/group"  component={Group} />
            </Router>
        </div>
    );
}
export default Example2;
3)利用useEffect实现基本的卸载
import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';

function Interface() {
    //新增
    useEffect(() => {
        console.log(`Welcome Interface!!!`);
        return ()=>{
            console.log("ByeBye Interface")
        } 
    })
    return <h2>接口测试</h2>
}

function Group() {
    //新增
    useEffect(() => {
        console.log(`Welcome Group!!!`);
        return ()=>{
            console.log("ByeBye Group")
        }
    })
    return <h2>组件测试</h2>
}

此时你会发现,虽然思想上实现了卸载的意义,但当点击click按钮时,仍然会发现ByeBye...会打印出来,其实每次状态发生变化,useEffect都进行了解绑。那么怎么实现类似于componentWillUnmount的效果呢?

4)利用useEffect的第二个参数实现卸载

也就是在useEffect函数中第一个参数是一个函数,第二个参数是一个数组,数组存放状态值,他的作用就是当状态值发生变化才会进行解绑,如果是一个空数组则表示,只当组件进行改变时进行解绑。代码如下:

function Interface() {
    useEffect(() => {
        console.log(`Welcome Interface!!!`);
        return ()=>{
            console.log("ByeBye Interface")
        } 
    },[])           //重点在这行
    return <h2>接口测试</h2>
}

function Group() {
    useEffect(() => {
        console.log(`Welcome Group!!!`);
        return ()=>{
            console.log("ByeBye Group")
        }
    },[])           //重点在这行
    return <h2>组件测试</h2>
}

5.useContext

用于解决父子组件传值,相当于props。
首先生成一个Context,利用createContext:

const TempContext  = createContext();

不过在此之前,需要先引入:

import React, { useState, useEffect, useContext, createContext } from 'React';

接着,如果想给子组件传temp值,要先定义一个提供其Provider:

<TempContext.Provider value={temp}>//里面的value属性的值就是存到了context的公共区域
    <Child />                       //引入Child子组件
</TempContext.Provider>

在子组件Child.js中:

function Child(){
    let temp = useContext(TempContext);
    return (<div>传给子组件:{temp}</div>)
}

效果图如下:

全部Example3.js代码:

import React, { useState, useEffect, createContext, useContext } from 'react';

const TempContext = createContext();

function Child(){
    let temp = useContext(TempContext);
    return(<div>传给子组件:{temp}</div>)
}

function Example3() {
    const [temp, setCount] = useState(0);
    useEffect(() => {
        console.log(`useEffect=>You clicked ${temp} times`);
    })
    return (
        <div>
            <div>
                clickResult is {temp}
            </div>
            <button onClick={() => { setCount(temp + 1) }} style={{ marginTop: "20px", marginBottom: "20px" }}>click this</button>
            <TempContext.Provider value ={temp}>
                <Child />
            </TempContext.Provider>
            
        </div>
    );
}


export default Example3;

6.useReducer

Reducer兴起于Redux,但与其无关,reducer是一个函数,可以这么定义:

function tempReducer (state,action){
    switch(action.type){
        case 'add':
            return state+1
        case 'sub':
            return state-1
        default:
            return state
    }
}

以上代码就是实现一个基本的Reducer,下面具体看一下useReducer

import React, { useState, useEffect, createContext, useContext, useReducer } from 'react';

function Example4() {
    // const [temp] = useState(0);
    const [temp, dispatch] = useReducer((state, action) => {
        switch (action) {
            case 'add':
                return state + 1
            case 'sub':
                return state - 1
            default:
                return state
        }
    },0)        //第二个参数为state的初始值,
    return (
        <div>
            <div>
                clickResult is {temp}
            </div>
            <button onClick={() => { dispatch('add') }} style={{ marginTop: "20px", marginBottom: "20px" }}>click add</button>
            <button onClick={() => { dispatch('sub') }} style={{ marginTop: "20px", marginBottom: "20px" }}>click sub</button>
            

        </div>
    );
}


export default Example4;

其中useReducer这个方法他有两个参数,第二个参数为默认值,他的返回值有两个,一个是状态,一个是分发器dispatch。dispatch是一个分发器,在redux中通常会这么写:

dispatch(
    type:xxx,
    value:xxx
)

效果图如下:

7.useContext和useReducer实现Redux

useContext:可访问全局状态。这符合Redux的状态全局化,能统一管理。
useReducer:通过action的传递,更新复杂逻辑的状态,主要是可以实现类似Redux中的Reducer部分,实现业务逻辑的可行性。
下面通过一个例子来详细说明:定义两个按钮来控制字体颜色,其中按钮放在一个组件,文字放在一个组件。效果图:

具体如下:

第一步:编写共享状态:

首先定义一个总得组件:Example6.js

import React, { useReducer } from 'react';
import Example5 from './Example5';
import Examplebtn1 from './Examplebtn1';
import { Color } from './Change';   //引入Color组件

function Example6 (){
    return(<div>
        <Color>                     //Color组件用于共享状态
            <Example5 />
            <Examplebtn1 />
        </Color>
    </div>)
}

再编写共享组件Color:

import React, { createContext } from 'react';

//首先先创建一个Context
export const ColorContext = createContext();

//再创建Color的共享状态
export const color = props=>{
    return(
        <ColorContext.Provider value={{color:"black"}}> //设置默认值black
            {props.children}                            //这里面的props.children指的是Color里面的每个组件
        </ColorContext.Provider>
    )
}

最后编写Example5.js

import React, { useContext } from 'react';
import { ColorContext } from './Change';

function Example5(){
    let {color} = useContext(ColorContext);
    return (<div style={{color:color}}>颜色为:{color}</div>)
}

export default Example5
第二步:编写usereducer实现状态值更新

定义reducer中的两个参数:
Change.js

import React, { createContext, useReducer } from 'react';

export const UPDAT_COLOR = "UPDAT_COLOR";

const  reducer = (state, action) =>{
    switch(action.type){
        case UPDATE_COLOR:
            return action.color
        default:
            return state
    }
}

export const Color = props=>{
    const  [color,dispatch] = useReducer(reducer,'blue')
    return(
        <ColorContext.Provider value={{color:color,dispatch}} >
            {props.children}
        </ColorContext.Provider>
    )
}

编写按钮组件Examplebtn1

import React, { useContext }  from 'react';
import { ColorContext, UPDATE_COLOR } from './Change'

function Examplebtn1(){
    const {dispatch} = useContext(ColorContext)
    return(
        <div>
            <button style={{marginTop:"20px",marginRight:"20px"}} onClick={()=>{dispatch({type:UPDATE_COLOR,color:"red"})}}>红色</button>
            <button onClick={()=>{dispatch({type:UPDATE_COLOR,color:"yellow"})}}>黄色</button>
        </div>
    )
}
export default Examplebtn1



代码详见:
github.com/gt3060/reac…