React Hooks的入门简介

什么是React Hooks?

首先React Hooks是React生态圈里的新特性,它改变了传统react的开发方式。Hooks本质上就是一类特殊的函数,他们可以为你的函数型组件注入一些特殊的功能,主要的改变点如下:

  1. 用函数的形式代替原本类继承的形式(不再使用class, 让function统一江湖)
  2. 改变原本复杂的状态操作,使用预函数的形式管理state
  3. 原本组件分为无状态组件(用函数的形式声明),和有状态组件(用类的形式声明),现在所有的组件都可以用函数声明。

react相关的hooks函数介绍

useState的介绍

useState是react自带的一个hooks函数,它的作用是用来声明状态变量。

const [ count , setCount ] = useState(0);根据这个表达式,简单的介绍一下useState
首先这种写法是是ES6语法中的数组解构。
useState这个函数接收的参数是状态的初始值(这句表达式表示count的初始值是0),它返回一个数组,这个数组的第0位是当前的状态值,第1位是可以改变状态值的方法函数。

// 下边这个小案例,就是实现useState的多状态声明,并实现点击按钮改变变量age的值

import React, { useState } from "react"; // 首先从react中引入useState

function Example1(){ // 使用函数的声明方式定义组件,而非使用继承的方式
  // 多状态声明 React是根据useState出现的顺序来确定的
  const [age, setAge]=useState(18);
  const [sex, setSex]=useState('nv');
  const [job, setJob]=useState('前端开发');

  return(
    <div>
      <p>今年{age}岁</p>
      <p>性别{sex}</p>
      <p>工作{job}</p>
      // 直接调用setAge函数,这个函数接收的参数是修改过的新状态值
      <button onClick={()=> {setAge(age+1)}}>加一岁</button>
    </div>
  )
}
export default Example1;

useState的使用时候的注意点:
在进行多状态声明的时候,React是根据useState出现的顺序来找到自己对应的state,
useState不能在if...else...这样的条件语句中进行调用,必须要按照相同的顺序进行渲染,即react hooks不能出现在条件判断语句中。小伙伴在使用的时候要多注意呦~

useEffect的介绍

在使用class定义组件的时候,react会为我们提供生命周期函数,用来处理一些额外的事情,hooks称这些生命周期是<font color="red">副作用</font>(就是和函数的主要业务逻辑关联不大的,需要在某些特定时刻进行执行的函数,比如我们经常使用到的通过Ajax获取远端的数据)。react hooks中也是提供了类似生命周期的副作用,就是使用useEffect来代替。

useEffect的初步认识
import React, { useState , useEffect } from 'react'; // 从react中引入要使用到的useState,useEffect
function Example2(){
    const [ count , setCount ] = useState(0);
    useEffect(()=>{
        console.log(`useEffect=>You clicked ${count} times`)
    })

    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={()=>{setCount(count+1)}}>click me</button>
        </div>
    )
}
export default Example2;

// 在浏览器中预览,我们会发现在组件第一次被渲染,以及变量发生改变的时候都会触发useEffect的副作用

做一个简单的总结:

  1. useEffect是可以代替之前react的componentDidMount和componentDidUpdate这两个生命周期
  2. useEffect是异步的,不会阻碍浏览器的视图更新, 没办法在页面变化的时候进行函数的执行。
    而componentDidMount和componentDidUpdate的执行是同步的。
useEffect来实现componentWillUnmount

在组件被卸载的时候我们经常会使用componentWillUnmount生命周期,来进行DOM的移除,定时器的清除,等等,从而做到防止内存泄露。useEffect也是可以实现这个生命周期的,hooks称作这个生命周期为<font color="red">解绑副作用</font>。我们还是用小案例进行解释哈~

为了可以演示出componentWillUnmount的效果,我们需要使用路由跳转,所以我们先进行简单的路由安装
npm install --save react-router-dom

// 相关代码如下,我们主要是为了演示解绑副作用的效果,所以路由跳转用到的组件,暂时不做拆分啦~
import React, { useEffect } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";

// 首先这是一个简单的首页组件
function Index(){
  useEffect(() => {
    console.log('useEffect => 欢迎进入 Index页面');
    return () => {  // 用返回一个函数的形式进行解绑
      console.log('离开Index页面')
    }
  }, []) // 但是当传空数组[]时,就是当组件将被销毁时才进行解绑,
 return (
   <h2>我是首页</h2>
 )
}

// 这是一个简单的列表组件
function List(){
  useEffect(() => {
    console.log('useEffect =>欢迎进入List页面')
  })
  return (
    <h2>我是列表页</h2>
  )
}

function Example3(){
  return(
    <div>
     <Router>
       <ul>
       <li> <Link to='/'>首页</Link></li>
       <li> <Link to='/list/'>列表页</Link></li>
       </ul>
      <Route path='/' exact component={Index}/>
      <Route path='/list/' component={List}/>
    </Router>
    </div>
  )
}
export default Example3;

简单的总结一下useEffect的解绑副作用

  1. 它是通过在传入的第一个参数即匿名函数中 <font color="red">通过返回一个函数的形式进行解绑</font>
  2. 它的第二个参数是一个数组,当传入的是空数组的时候,只有当组件销毁的时候才进行解绑,这也就实现了componentWillUnmount的生命周期函数;当在数组中传入状态对应的变量时,意思是当状态值发生变化时,我们才进行解绑。

useContext的介绍

之前使用的继承的形式来生命组件,我们可以通过constructor中的props进行父子组件传值,当我们使用了函数来定义组件,并没有constructor构造函数了,所以我们需要使用hooks提供的useContext进行跨越组件层级直接传递变量,实现共享。它主要解决的是组件之间值传递的问题。

// 同上一个Demo,这里我们仅仅是为了实现父子组件的传值,为了代码更加好阅读,所以涉及到的组件暂时不做拆分

import React, { useState, createContext, useContext } from 'react'; // 引入相关的hooks函数

const CountContext = createContext(); // ① 核心代码 创建共享的组件(上下文), 用来包裹子组件

function ChildCom(){
  let count = useContext(CountContext); // ③ 子组件接受值
  return (<h2>{count}</h2>)
}

function Example4(){
const [count, setCount] = useState(0);
 return (
  <div>
    <p>display {count}</p>
    <button onClick={() => {setCount(count + 1)}}>click {count}</button>
    
    {/* ② 给子组件共享值 */}
    <CountContext.Provider value={count}> 
      {/* value是要共享的值, 这里包裹的是要分享给的子组件,相当于把count分享给子组件 */}
      <ChildCom/>
    </CountContext.Provider>
    
  </div>
  )
}

export default Example4;

简单总结父子组件传值的步骤

  1. 使用Test = createContext()创建共享组件上下文,用来包裹子组件,比如这里创建的名字是Test。
  2. 通过上下文标签包裹子组件,进行数据的共享。
  3. 子组件通过使用useContext(Test)获取父组件传递过来的值。

userReducer的介绍

我们先简单的讲一下Redux中的reducer,reducer其实就是一个普通的函数,这个函数接受2个参数,一个是状态,一个是控制业务逻辑的的判断参数,更通俗一点就是,一个是状态,一个是控制状态。userReducer和reducer很类似,我们通过一个实现增加和减少数据的案例来演示一下它的使用~

useReducer更新逻辑状态的示例
import React, { useReducer } from 'react'; // 引入hooks的useReducer函数

function ReducerDemo(){
  // 传入两个参数 ① 相关执行的方法 ② 变量的初始值
  // 返回两个参数 ① 通过相关业务逻辑后返回的值这里是count  
                ② 第二个参数是个派发器,通过传递action走不同的业务逻辑
  const [count, dispatch] = useReducer((state, action) => {// 这个函数的参数 一个是状态,一个是如何控制状态
    switch(action){
      case 'add':
        return state + 1;
      case 'sub':
        return state - 1;
      default:
        return state;
    }
  }, 0)

  return (
    <div>
      <h2>现在的分数是{count}</h2>
      <button onClick={() => {dispatch('add')}}>Increment</button>
      <button onClick={() => {dispatch('sub')}}>Descrement</button>
    </div>
  )
}

export default ReducerDemo;

useReducer与useContext实现Redux的示例

这里主要想通过点击按钮来实现页面文字颜色切换的功能。这里的一个核心组件就是用来管理颜色的和处理颜色的Color.js组件。本次示例只要使用的组件有:管理颜色的Color.js组件;操作颜色变化的Button.js组件;显示文字的ShowText组件;以及他们的父组件Combine.js。具体逻辑请看一下代码~

// 核心组件Color组件(拥有共享状态和处理业务能力的组件)
import React, {createContext, useReducer} from 'react';

export const ColorContext = createContext({}); // 将属性color共享出去

// 封装useReducer的中要执行的相关方法
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 (
    <ColorContext.Provider value={{color, dispatch}}> //共享出去的状态变成了color和dispatch
      {props.children}  // 使用 {props.children}来显示对应的子组件
    </ColorContext.Provider>
  )
}

// 切换颜色的Button组件
import React, {useContext} from 'react';
import { ColorContext, UPDATE_COLOR } from './color';

function Buttons(){
  const {dispatch} = useContext(ColorContext); // 使用color组件,共享出来的
  return (
    <div>
      <button onClick={() => {dispatch({type: UPDATE_COLOR, color: 'red'})}}>红色</button>
      <button onClick={() => {dispatch({type: UPDATE_COLOR, color: 'yellow'})}}>黄色</button>
    </div>
  )
}

export default Buttons;


// 显示文字的区域
import React, {useContext} from 'react';
import { ColorContext } from './color'; 

function ShowText(){
  const {color} = useContext(ColorContext); // 也是使用共享出来的颜色变量
  return (
    <div style={{color: color}}>
      字体颜色为{color}
    </div>
  )
}

export default ShowText;


// 以上子组件共同的父组件
import React from 'react';
import ShowText from './showText';
import Buttons from './buttons';
import { Color } from './color';

function Combine(){
  return (
    <div>
      <Color>
       // ShowText和Buttons当做是Color的子组件,ShowText共享到color属性,Buttons操作颜色的变化
        <ShowText/>
        <Buttons/>
      </Color>
    </div>
  )
}

export default Combine;

这种useReducer与useContext实现Redux的效果,比之前的Redux简单些,但是还是有一定难度的,希望这个小案例可以帮助大家更好的理解useReducer和useContext吧~

useMemo的介绍

之前我们在介绍react生命周期的时候,有提到过shouldComponentUpdate,我们可以在组件更新之前,在这个生命周期中对比状态,只有真正需要更新的时候才更新,可是hooks中的useEffect没有这个功能,所以会导致只要父组件更新,子组件里的方法都会执行一次,严重影响性能。 这个时候hooks给我们提供了useMemo,它主要用来解决使用React hooks产生的无用渲染的性能问题。

// 这个案例主要通过在父组件中点击不同的按钮,显示当前的时间戳,从而来验证父组件变化是否会影响子组件的变化
import React, { useState, useMemo } from 'react'; // 首先引入useMemo函数

function MemoDemo(){
  const [reactTime, setReactTime] = useState('学习React');
  const [ngTime, setNgTime] = useState('学习Angular');
  return (
    <>
     <button onClick={() => {setReactTime(new Date().getTime())}}>React</button>
     <button onClick={() => {setNgTime(new Date().getTime()+'Angular is studying')}}>Angular</button>
     <ChildComponent name={reactTime}> { ngTime } </ChildComponent>
    </>
  )
}

// 新建一个子组件
function ChildComponent({name, time}){
  // 如果不使用useMemo,只要父组件发生变化,这个函数都会执行的
  function changeReactTime(){ 
    console.log('she is coming, React');
    return name+',React is coming';
  }

 //由于传入的name是reactTime,所以只有reactTime改变的时候,子组件的这个方法才会执行。
  const actionReact = useMemo(() => changeReactTime(name), [name]); 
  return (
    <>
      <div>{actionReact}</div>
      <div>{time}</div>
    </>
  )
}

export default MemoDemo;

总结: useMemo函数需要传入两个参数,第一个是本来就要执行的函数,第二个是决定这个函数是否要执行的变量~ 只有第二个参数匹配成功,才会执行第一个参数中的函数。

useCallback的介绍

之前我们介绍到useMemo是为了缓存变量,这里我们介绍的usecallback是为了缓存方法。我们可以通过自定义hooks函数来使用一下useCallback,我们可以通过自定义hooks函数来实现一个获取窗口大小的函数。需要注意的一点是自定义的hooks函数需要使用use开头。(这块内容我并没有深入的了解,所以如有不足,请大家多多包涵哈~)

import React, { useState ,useEffect ,useCallback } from 'react';
// 自定的useWinSiz hooks函数
function useWinSize(){
  const [ size , setSize] = useState({
      width:document.documentElement.clientWidth,
      height:document.documentElement.clientHeight
  })

  const onResize = useCallback(()=>{
      setSize({
          width: document.documentElement.clientWidth,
          height: document.documentElement.clientHeight
      })
  },[]) 
  
  useEffect(()=>{
      window.addEventListener('resize',onResize)
      return ()=>{
          window.removeEventListener('resize',onResize)
      }
  },[])

  return size;

}


function CallBackDemo(){
  const size = useWinSize()
  return (
      <div>页面Size:{size.width}x{size.height}</div>
  )
}

export default CallBackDemo; 

总结

react hooks就介绍到这里啦,其中这些案例我也就简单的敲过一遍,由于没有实质项目的编写经验,所以肯定还是有很多不足的地方,假如介绍的内容有错误的地方,还请大家多多指正哈~

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

推荐阅读更多精彩内容