React-Hooks带来了什么?

对应的代码仓库:https://codesandbox.io/s/learning-react-hooks-7ssri

其实对应的React-Hooks带来的东西很多,其中有什么我们简单讲解一下:

  • 使用函数的形式代替原来的继承类的形式!
  • 使用预函数的形式管理对应的state!
  • 可以使用React-Hooks来创建带状态的组件而并非使用类!

那么对应的我们使用简单的代码实例学习React-Hooks! 本文中使用的是CodeSandbox而并非使用 create-react-app创建对应的React项目!

对应的 src/index.js代码如下所示:

import React from "react";
import ReactDOM from "react-dom";
ReactDOM.render(<App/>,document.querySelector("#root"));

01|传统的类形式的组件与React-Hooks组件进行对比

import React,{Component} from "react";

class Example extends Component{
    constructor(props){
        super(props);
        this.state = {count:0};
        this.addCount = this.addCount.bind(this);
    }
    addCount(){
        this.setState({count:this.state.count+1})
    }
    render(){
        return (
            <div>
                <span>You clicked {this.state.count} times</span>
                <button onClick={this.addCount}>Click me</button>
            </div>
        );
    }
}
export default Example;

其实上面的代码很清楚的就解释了一个最基本的组件是如何的!

下面看看最基本的React-Hooks的组件是如何的!

import React,{useState} from "react";
function Example(){
    const [count,setCount] = useState(0);
    return (
        <div>
            <span>You clicked {count} times</span>
            <button onClick={()=>{setCount(count+1)}}>Click me</button>
        </div>
    );
}
export default Example;

其实从对应的单词Hooks中我们就可以知道本身就是钩子的意思! 为的就是让你不在写Class,而是使用function更好的写React!

其中引入的useState其实就是所谓的状态使用,对应的count表示对应的状态setCount很明显就是可以改变状态的方法而使用useState(0)表示当前的count值为0

那么如何理解呢? 在对应的代码中的表示?

  • useState()接收对应的初始值返回一个数组
  • 数组的第一位也就是所谓的count表示状态变量count
  • 数组的第二位表示设置状态变量的方法setCount

02|React-Hooks的多状态声明

对应的如何使用多状态声明? 上代码

import React,{useState} from "react";
function Example2(){
    const [age,setAge] = useState(22);
    const [sex,setSex] = useState("男");
    const [work,setWork] = useState("frontEnd");
    return (
        <div>
            <p>ProbeDream:今年{age}岁</p>
            <p>性别:{sex}</p>
            <p>工作:{work}</p>
        </div>
    );
}
export default Example2;

其中别人可能会问,如何确定对应的useState找到自己的state呢? React是根据useState出现的顺序确定的!

import React,{useState} from "react";
let showSex = true;
function Example3(){
    const [age,setAge] = useState(22);
    if(showSex){
        const [sex,setSex] = useState("男");
        showSex = false;
    }
    const [work,setWork] = useState("frontEnd");
    return (
        <div>
            <p>ProbeDream:年龄{age}岁</p>
            <p>性别:{sex}</p>
            <p>工作:{work}</p>
        </div>
    );
}
export default Example3;

如果说你写下这一行代码的话,会出现对应的错误就是,useState不能够在ifelse这种条件判断语句中使用! 不然的话会报错的!

由此我们可以知道:React Hooks不能出现在条件判断语句中,因为它必须有完全一样的渲染顺序!

03|useEffect代替常用生命周期函数

其实在我们写React的时候使用class制作组件的时候,会经常使用到生命周期函数来处理特定阶段的事情!

例如说:

  • Ajax请求后端的数据
  • 添加登录监听和取消登录
  • 手动修改DOM等等

那么对应的问题来了? 如果说使用React-Hooks的话有没有对应的生命周期函数或者说类似于这种功能的方法?

有的,React-Hooks中提供了一种方法叫做useEffect来帮我们解决日常开发中的问题!

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

function Example03() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log(`effect=>You Clicked ${count} times!`);
  });
  return (
    <div>
      <p>You Clicked {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click me!
      </button>
    </div>
  );
}
export default Example03;

通过对应的代码示例便可以知道对应的效果! 我们梳理一下过程

  • 我们声明一个组件,并且给定状态count为初始值0 并且告诉React有一个副作用!
  • 给了useEffect函数里面的匿名函数就是副作用,我们手动更新DOM就会调用副作用一次
  • 等到React更新了State状态的时候就会执行定义的副作用函数!

useEffecct需要注意的点在哪里

  1. React首次渲染和每次更新都会调用useEffect函数,而之前如果说使用class的话就会调用两个函数分别为 ComponentDidMount和ComponentDidUpdate
  2. useEffect定义的函数的执行不会阻碍浏览器更新视图! 对应的函数是异步执行的! 而在class组件中的话ComponentDidMount和ComponentDidUpdate都是同步执行的!

04|使用useEffect实现ComponentWillUnmount生命周期函数

对应的其实这一个生命周期函数还是比较常见的函数,表示组件卸载销毁之前调用! 其中对应的应用场景还是比较丰富的! 例如说:

  • 定时器清空,避免发生内存泄露
  • 登录状态的取消操作,避免下次进入信息出错!

因此在这里就简单介绍一下使用useEffect实现这个函数!并且介绍useeffect对应的需要注意的地方!

  • useEffect解绑副作用

对应的在React-Hooks中我们应该改掉所谓的生命周期函数的概念,取而代之的是 副作用 的概念! 因此对应的ComponentWillUnmount可以理解成为解绑副作用! 在这里为了更好地理解这些东西,我们需要使用react-router

yarn add react-router-dom

对应的路由表配置也在对应的Example04.jsx中完成:

import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Index, List } from "../components/routerComponent";

export default function Example04() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>Your Clicked {count} times!</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click me!
      </button>
      <Router>
        <ul>
          <li>
            <Link to="/">首页</Link>
          </li>
          <li>
            <Link to="/list/">列表</Link>
          </li>
        </ul>
        <Route exact path="/" component={Index} />
        <Route path="/list/" component={List} />
      </Router>
    </div>
  );
}

对应的路由组件的内容如下所示:

import React, { useEffect } from "react";
export function Index() {
  useEffect(()=>{
    console.log(`useEffect=>老弟你来了Index界面!`)
  });
  return <h1>Github.com/probedream</h1>;
}
export function List() {
  useEffect(()=>{
    console.log(`useEffect=>老弟你来了List界面!`)
  });
  return <h1>List-page</h1>;
}

对应的当我们没当点击一个link的时候都会通过useEffect副作用打印出一个内容,对应的打印出对应的内容! 此时我们可以通过返回一个函数的形式进行解绑操作! 此时的代码如下所示:

import React, { useEffect } from "react";
export function Index() {
  useEffect(() => {
    console.log(`useEffect=>老弟你来了Index界面!`);
    return () => {
      console.log(`老弟你走了离开了Index页面!`);
    };
  });
  return <h1>Github.com/probedream</h1>;
}
export function List() {
  useEffect(() => {
    console.log(`useEffect=>老弟你来了List界面!`);
    return () => {
      console.log(`老弟你走了离开了List页面!`);
    };
  });
  return <h1>List-page</h1>;
}

但是修改了对应的代码之后会发现没次点击都会出现 老弟你走了离开了Index页面! useEffect=>老弟你来了Index界面!的情况!

对应的每次状态发生变化的时候,useEffect都进行了解绑操作!

  • useEffect的第二个参数

为了达到Class组件中生命周期 ComponentWillUnmount函数的效果,只有对应组件被销毁和卸载之前进行操作,由此我们引申出useEffect的第二个参数:

  • 是一个数组,数组中可以写入很多状态对应的变量! 只有对应状态发生变化的时候我们才进行解绑操作!
  • 传入一个空数组的时候,空数组被销毁的时候才会进行解绑操作! 由此才像ComponentWillUnmount生命周期函数一样!

由此代码进行了下一步的演变:

import React, { useEffect } from "react";
export function Index() {
  useEffect(() => {
    console.log(`useEffect=>老弟你来了Index界面!`);
    return () => {
      console.log(`老弟你走了离开了Index页面!`);
    };
  }, []);
  return <h1>Github.com/probedream</h1>;
}
export function List() {
  useEffect(() => {
    console.log(`useEffect=>老弟你来了List界面!`);
    return () => {
      console.log(`老弟你走了离开了List页面!`);
    };
  }, []);
  return <h1>List-page</h1>;
}

其中我们根据对应的一些代码,状态的变化会导致副作用的解绑!

如果说计时器中加入对应的代码呢?

import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Index, List } from "../components/routerComponent";

export default function Example04() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log(`Your Clicked ${count} times!`);
    return () => {
      console.log("=============");
    };
  }, []);
  return (
    <div>
      <p>Your Clicked {count} times!</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click me!
      </button>
      <Router>
        <ul>
          <li>
            <Link to="/">首页</Link>
          </li>
          <li>
            <Link to="/list/">列表</Link>
          </li>
        </ul>
        <Route exact path="/" component={Index} />
        <Route path="/list/" component={List} />
      </Router>
    </div>
  );
}

如果说每次都解绑的话,需要 不传入第二个参数或者如下所示:

import React,{useEffect,useState} from "react";
export default function Example04(){
    const [count,setCount] = useState(0);
    useEffect(()=>{
        console.log(`Your clicked ${count} times`);
        return ()=>{console.log("=============");}
    },[count]);
    return (
        <div>
            <p>Your Clicked {count} times</p>
            <button onClick={()=>setCount(count+1)}>Click me</button>
        </div>
    );
}

05|使用useContext让父子组件传值更加简单

虽然说使用useState和useEffect就已经能够完成大部分的业务了,但是React-Hooks中还是有很多好用的API,比如说:useContext和useReducer

其中因为现在组件的方式使用的是函数的形式,不像Class的类组件一样! 那么对应的父子组件之间如何进行传值操作呢? 这里就需要介绍到我们的useContext了!

  • useContext解决的是跨组件通信的问题
  • Redux解决的是状态统一集中管理的问题

之后会介绍到了useReducer帮助我们实现类似于Redux的功能!

其中的代码其实比较好理解,代码如下所示:

import React from "react";
import ReactDOM from "react-dom";
import {Example05} from "../components/Example05";
ReactDOM.render(<Example05/>,document.querySelector("#root"));
import React,{useState,createContext,useContext} from "react";
const CountContext = createContext();
export function Example05(){
    const [count,setCount] = useState(0);
    return (
        <div>
            <p>Your Clicked {count} times!</p>
            <button onClick={()=>{setCount(count+1)}}>Click me!</button>
            <CountContext.Provider value={count}>
                <Counter/>
            </CountContext.Provider>
        </div>
    );
}
export function Counter(){
    const count = useContext(CountContext);
    return <h3>{count}</h3>
}

从对应的代码中应该不难看出对应的代码的作用!

  • createContext:创建对应的上下文对象 通过对应的Provider提供者 传递对应的状态给子组件! 传入的参数为default 默认值
  • useContext:使用上下文 **传入对应的上下文对象可以拿到提供者对象传递的变量

06|useReducer介绍和简单使用

在开发者使用useReducer可以让代码有很好的可读性和可维护性! 为对应的测试提供方便!

reducer之所以在前端广泛使用,是因为Redux的缘故,但是并不是只存在于Redux中! 对应的reducer只是一个函数,传入两个参数:

  • 一个参数是状态
  • 一个参数是控制业务逻辑的判断参数

也许这么说可能不那么清楚,但是如果说通过对应的代码来看的话,可能会好很多

function countReducer(state,action){
    switch(action.type){
        case "add":
            return state + 1;
        case "minus":
            return state - 1;
        default:
            return state;
    }
}

通过以上代码变很好地说明了两个参数之间的差异性!

01|useReducer的使用
import React,{useReducer} from "react";
export default function Example06(){
    const [count,dispatch] = useReducer((state,action)=>{
        switch(action){
            case "add":return state + 1;
            case "minus":return state - 1;
            default : return state;    
        }
    },0);
    return (
        <div>
            <p>现在的分数为:{count}</p>
            <button onClick={()=>dispatch("add")}>Increment</button>
            <button onClick={()=>dispatch("minus")}>Decrement</button>
        </div>
    );
}

其中需要注意的两个点:

就是reducer和useReducer的区别还是有的,例如说switch中的其实是action,在reducer种的是action.type,对应的state中的初始值是在 useReducer中的第二个参数定义的!

08|useReducer代替Redux案例

对应程序的入口文件main.js:

import React from "react";
import ReactDOM from "react-dom";
import Example from "../Example07/Example07";

ReactDOM.render(<Example07/>,document.querySelector("#root"));

现将程序挂载到首页容器节点上!

import React from "react";
import { Color } from "./Color";
import ShowArea from "./ShowArea";
import Button from "./Button";

export default function Example07() {
  return (
    <div>
      <Color>
        <ShowArea />
        <Button />
      </Color>
    </div>
  );
}

其中涉及到三个组件:

  • showArea:显示字体
  • Button:按钮组件
  • Color:颜色控制组件
import React, { createContext, useReducer } from "react";

export const ColorContext = createContext({});

export const UPDATE_COLOR = "UPDATE_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 (
    <div>
      <ColorContext.Provider value={{ color, dispatch }}>
        {props.children}
      </ColorContext.Provider>
    </div>
  );
};

这里主要是对颜色进行统一设置,由此就涉及到了 前文中讲到的内容:createContext和useReducer

  • createContext:用来创建上下文对象
  • useReducer:用来创建操作器

这个组件当中向外暴露了三个常量,分别为:

  • ColorContext:颜色上下文对象 传入为一个空对象 子组件通过useContext拿到的也是空对象!
  • reducer:作为一个函数根据对应的参数类型判断操作数据
  • Color:作为跨层传递内容的组件 将对应的内容传递给子组件
import React, { useContext } from "react";
import { UPDATE_COLOR, ColorContext } from "./Color";

export default function Buttons() {
  const { dispatch } = useContext(ColorContext);
  return (
    <div>
      <button onClick={() => dispatch({ type: UPDATE_COLOR, color: "red" })}>
        红色
      </button>
      <button onClick={() => dispatch({ type: UPDATE_COLOR, color: "yellow" })}>
        黄色
      </button>
    </div>
  );
}
  • 引入对应的UPDATE_COLOR常量和颜色上下文!
import React, { useContext } from "react";
import { ColorContext } from "./Color";
export default function ShowArea() {
  const { color } = useContext(ColorContext);
  return <div style={{ color: color }}>字体颜色为blue</div>;
}

其中我通过以下代码打印处获取到的Context对象如下所示:

import React,{useContext} from "react";
import {ColorContext} from "./Color";
console.log(useContext(ColorContext)); // {color,dispatch}

由此不难推导出通过useContext拿到的对象便是value {color,dispatch}对象!

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

export const UPDATE_COLOR = "UPDATE_COLOR";

export const ColorContext = createContext({});

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 (
    <div>
        <ColorContext.Provider value={{color,dispatch}}>
            <props.children/>
        </ColorContext.Provider>
    </div>
    );
}

09|使用useMemo优化React-Hooks的程序

import React, { useState } from "react";
import ChildComponent from "./ChildComponent";

export default function Example08() {
  const [xiaoming, setXM] = useState("小明准备中");
  const [xiaowang, setXW] = useState("小王准备中");
  return (
    <div>
      <button
        onClick={() => {
          setXM(new Date().getTime());
        }}
      >
        小明
      </button>
      <button
        onClick={() => {
          setXW(new Date().getDate() + ",小王向我们走来");
        }}
      >
        小王
      </button>
      <ChildComponent name={xiaoming}>{xiaowang}</ChildComponent>
    </div>
  );
}

childComponent

import React from "react";

export default function ChildComponent({ name, children }) {
  function changeXM(name) {
    console.log("小明正在大步向前");
    return name + "小明加油啊!";
  }
  const actionXM = changeXM(name);
  return (
    <div>
      <div>{actionXM}</div>
      <div>{children}</div>
    </div>
  );
}

此时打开对应的程序,你会发现点击小王,总是会出现小明正在大步向前每次都运行何尝不是性能的损耗呢? 因此我们决定使用 新的API useMemo予以解决!

import React,{useMemo} from "reat";
  useMemo(()=>changeXM(name),[name]);

更多的特性讲解updating......

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容