美文网首页
React Hooks && 其它补充知识点

React Hooks && 其它补充知识点

作者: alicemum | 来源:发表于2021-04-25 19:58 被阅读0次

组件类的缺点

React 的核心是组件。v16.8 版本之前,组件的标准写法是类(class)。
下面是一个简单的组件类。

import React, { Component } from "react";

export default class Button extends Component {
  constructor() {
    super();
    this.state = { buttonText: "Click me, please" };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState(() => {
      return { buttonText: "Thanks, been clicked!" };
    });
  }
  render() {
    const { buttonText } = this.state;
    return <button onClick={this.handleClick}>{buttonText}</button>;
  }
}

这个组件类仅仅是一个按钮,但可以看到,它的代码已经很"重"了。真实的 React App 由多个类按照层级,一层层构成,复杂度成倍增长。再加入 Redux,就变得更复杂。

Redux 的作者 总结了组件类的几个缺点。

  • 大型组件很难拆分和重构,也很难测试。
  • 业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。
  • 组件类引入了复杂的编程模式

函数组件

React 团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。 组件的最佳写法应该是函数,而不是类。

React 早就支持函数组件,下面就是一个例子。

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

但是,这种写法有重大限制,必须是纯函数,不能包含状态,也不支持生命周期方法,因此无法取代类。

React Hooks

React Hooks 的设计目的,就是加强版函数组件,完全不使用"类",就能写出一个全功能的组件。

Hook 这个单词的意思是"钩子"。

React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。 React Hooks 就是那些钩子。

React 常用Hook

  • useState
  • useEffect
  • useRef
  • useContext
  • useReducer
  • useCallback
  • useMemo

useState

  • useState()用于为函数组件引入状态(state)。纯函数不能有状态,所以把状态放在钩子里面。
//引入钩子函数
import {useState} from 'react';
const Test = () => {
    // useState的参数为状态初始值
    // useSibtate的返回值是一个数组
    // 数组第一个成员是变量,第二个成员是函数,用来更新状态,约定是set前缀加上状态的变量名
    const [num,setNum] = useState(1)
    return (
        <div>
            <div>访问num: {num}</div>
            <button onClick={()=>setNum(num+1)}>修改state</button>
        </div>
    );
}
export default Test

useEffect副作用钩子

useEffect()用来引入具有副作用的操作,最常见的就是向服务器请求数据。
useEffect() 可以实现生命周期函数的效果

import { useState, useEffect } from 'react';
const Test = () => {
    const [num, setNum] = useState(1)
    const [msg, setMsg] = useState('a')
    const [list, setList] = useState([])

    // 组件初始化自动执行一次,之后每更新一次执行一次
    // 类似 componentDidUpdate
    useEffect(()=>{
        console.log('没有第二个参数,多次执行');
    })

    //等价于 componentDidMount , 只在初始化时执行一次
    useEffect(()=>{
        console.log('第二个参数为空数组,只执行一次');
    },[])

    // 第二个参数为state, 初次执行后,只有该state改变时执行
    useEffect(()=>{
        console.log('第二个参数为state数据,数据改变一次执行一次');
    },[num])

    useEffect(()=>{
        //return的回调 等价于 componentWillUnmount() 组件卸载时
        return ()=>{
            console.log('组件卸载时');
        }
    },[])
    return (
        <div>
            <div>访问num: {num}</div>
            <button onClick={() => setNum(num + 1)}>修改num</button>
            <hr/>
            <div>访问msg: {msg}</div>
            <button onClick={() => setMsg(msg + 'm')}>修改msg</button>
        </div>
    );
}
export default Test

在父组件中实现组件卸载

import Test from './Test'
import { useState } from 'react';

const App = () => {
  const [flag,setFlag] = useState(true)
  return ( 
    <div>
      {/* 单击按钮,卸载组件 */}
      <button onClick={()=>{setFlag(false)}}>删除Test</button>
      { flag ? <Test></Test> : null}
    </div>
   );
}

useRef

useRef用来获取DOM元素

import { useRef , useEffect} from "react";

const Test = () => {
    //1. 创建引用实例
    const btnRef = useRef()
    useEffect(()=>{
        //3. 通过实例的current属性即可访问DOM对象
        btnRef.current.focus()
    },[])
    return (
        <div>
            {/* 2. 给DOM元素添加ref属性,值为useRef创建的实例 */}
            <input type="text"  ref={btnRef}/>
        </div>
    );
}
export default Test

useContext 共享状态钩子

如果需要在组件之间共享状态,可以使用useContext()

  1. 使用 React Context API,在组件外部建立一个 Context
    Context因为需要其它组件共享,所以要单独导出
export const AppContext = createContext()   
  1. 利用父组件状态钩子,创建共享状态
const [city,setCity] = useState('郑州')   
  1. 提供了一个 Context 对象,这个对象可以被子组件共享

value值为共享状态,值为一个对象

<AppContext.Provider value={{city,setCity}}>
    Test---{city}
    <hr/>
    <Child></Child>
</AppContext.Provider>

4、在后代组件中引入Context,

import {AppContext} from './Test'
  1. useContext()钩子函数用来引入 Context 对象,从中获取所需要的状态
import React,{useContext} from 'react';
......
const {city,setCity} = useContext(AppContext)

完整案例

Parent.js

import { createContext, useState } from 'react'
import Child from './Child'
export const AppContext = createContext()
const Parent = () => {
    const [city, setCity] = useState('郑州')
    return (
        <AppContext.Provider value={{ city, setCity }}>
            Parent---{city}
            <hr />
            <Child></Child>
        </AppContext.Provider>
    );
}
export default Parent;

Child.js

import GrandSon from './GrandSon'
const Child = () => {
    return ( 
        <div>
            Child
            <hr/>
            <GrandSon></GrandSon>
        </div>
     );
} 
export default Child;

GrandSon.js

import React,{useContext} from 'react';
import {AppContext} from './Test'

const GrandSon = () => {
    const {city,setCity} = useContext(AppContext)
    return ( 
        <div>
            GrandSon-----{city}
            <div><button onClick={()=>{setCity('广州')}}>改变城市</button></div>
            <hr/>
        </div>
     );
}
export default GrandSon;

useReducer

  1. useReducer 是用于提高应用性能的,当更新逻辑比较复杂时,我们应该考虑使用useReducer
  2. useReducer 是 useState的代替方案,用于 state 复杂变化
  3. useReducer 是单个组件状态管理,组件通讯还需要 props
  4. redux 是全局状态管理,多组件共享数据

useReducer 接受一个 reducer 函数作为参数,
reducer 接受两个参数一个是 state 另一个是 action 。然后返回一个状态 count 和 dispath,count 是返回状态中的值,
而 dispatch 可以发布事件来更新 state 。

import { useReducer } from 'react'

// 定义reducter,不像redux传递type
const reducer = (state,action)=> {
    switch(action){
        case 'add':
             return state + 1;
         case 'sub':
             return state - 1;
         default:
             return state;
    }
 }
const Parent = () => {
    // count为变量即状态 , dispatch用来发布事件改变状态 
    // useReducer第二个参数为count的初始值
    const [count, dispatch] = useReducer(reducer,3)
    return (
        <div>
            <div>count------{count}</div>
            {/* 用dispatch发布事件 */}
            <div><button onClick={()=>{dispatch('add')}}>add</button></div>
            <div><button onClick={()=>{dispatch('sub')}}>sub</button></div>
        </div>
    );
}

export default Parent;

useMemo

函数组件的每一次调用都会执行其内部的所有逻辑,那么会带来较大的性能损耗。因此useMemo 和useCallback就是解决性能问题的杀手锏。

useCallback和useMemo的参数跟useEffect一致,他们之间最大的区别有是useEffect会用于处理副作用,而前两个hooks不能。

useMemo和useCallback都会在组件第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行;并且这两个hooks都返回缓存的值,useMemo返回缓存的变量,useCallback返回缓存的函数。
反例: 不相关的val改变,expensive也会执行

import {useState} from 'react';
 
export default function WithoutMemo() {
    const [count, setCount] = useState(1);
    const [val, setValue] = useState('');
 
    function expensive() {
        console.log('compute');
        let sum = 0;
        for (let i = 0; i < count * 100; i++) {
            sum += i;
        }
        return sum;
    }
 
    return <div>
        <h4>{count}-{val}-{expensive()}</h4>
        <div>
            <button onClick={() => setCount(count + 1)}>+c1</button>
            <input value={val} onChange={event => setValue(event.target.value)}/>
        </div>
    </div>;
}

用useMemo解决

import {useState,useMemo} from 'react';
 
 
export default function WithMemo() {
    const [count, setCount] = useState(1);
    const [val, setValue] = useState('');

    // useMemo第一个参数是回调, 第二个是依赖的变量
    const expensive = useMemo(() => {
        console.log('compute');
        let sum = 0;
        for (let i = 0; i < count * 100; i++) {
            sum += i;
        }
        return sum;
    }, [count]);
 
    return <div>
        <h4>{count}-{expensive}</h4>
        {val}
        <div>
            <button onClick={() => setCount(count + 1)}>+c1</button>
            <input value={val} onChange={event => setValue(event.target.value)}/>
        </div>
    </div>;
}

useCallback

useCallback跟useMemo比较类似,但它返回的是缓存的函数。

分析下面的案例:
每次修改count,set.size就会+1,这说明useCallback依赖变量count,count变更时会返回新的函数;而val变更时,set.size不会变,说明返回的是缓存的旧版本函数。

import React, { useState, useCallback } from 'react';
 
const set = new Set();
 
export default function Callback() {
    const [count, setCount] = useState(1);
    const [val, setVal] = useState('');
 
    const callback = useCallback(() => {
        console.log(count);
    }, [count]);
    set.add(callback);
    return <div>
        <h4>{count}</h4>
        <h4>{set.size}</h4>
        <div>
            <button onClick={() => setCount(count + 1)}>+</button>
            <input value={val} onChange={event => setVal(event.target.value)}/>
        </div>
    </div>;
}

使用场景是:有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新。

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

export default function Parent() {
    const [count, setCount] = useState(1);
    const [val, setVal] = useState('');
 
    //观察子组件userEffect在val值改变时不触发
    // const callback = useCallback(() => {
    //     console.log('cb');
    //     return count;
    // }, [count]);

    //观察子组件userEffect在val值改变时触发
    const callback = () => {
        console.log('cb');
        return count;
    };

    return <div>
        <h4>{count}</h4>
        <Child callback={callback}/>
        <div>
            <button onClick={() => setCount(count + 1)}>+</button>
            <input value={val} onChange={event => setVal(event.target.value)}/>
        </div>
    </div>;
}
 
function Child({ callback }) {
    console.log('child');
    const [count, setCount] = useState(() => callback());
    useEffect(() => {
        console.log('useeffect');
        setCount(callback());
    }, [callback]);
    return <div>
        子------{count}
    </div>
}

React-router 常用Hook

  • useParams
  • useHistory
  • useLocation
import { useEffect } from 'react'
import { useParams,useLocation,useHistory } from 'react-router-dom'
const Star = () => {
    //获取路由信息中location数据
    console.log('useLocation',useLocation())
    //获取路由信息中history,调用history中的方法,可实现编程式导航
    console.log('useHistory',useHistory())

    //获取动态路由的参数
    let { id } = useParams()
    //组件初始化时,获取id,请求数据
    useEffect(() => {
        console.log('获取' + id + '对应的数据')
    }, [])

    return (
        <div>Star</div>
    );
}

export default Star;

WithRouter的用法

默认只有路由组件的props才能获取路由信息,普通组件如何要获取路由信息,实现编程式导航,可以用withRouter来实现路由的注入

import { withRouter } from 'react-router-dom'
const CartChild = (props) => {
    console.log('CartChild', props)
    return (
        <div>
            CartChild
            <hr />
            <button onClick={() => { }}>回到首页</button>
        </div>
    );
}

export default withRouter(CartChild);

React.createElement的用法

调用 React.createElement 返回的是js对象

import React, { Component } from 'react';
import ReactDOM from 'react-dom'


let obj = React.createElement(   
  "div",
  {
    className: "page",
  },
  React.createElement('header', null, " Hello, This is React "),
  React.createElement("h1", null, "Start to learn right now!"),
  "Right Reserve."
);

ReactDOM.render(obj, document.getElementById("root"));

props.children的用法

类似vue中的插槽

父组件

import CartChild from './CartChild'
const Cart = () => {
    return (
        <div>
            Cart
            <hr />
            <CartChild>
                <div>
                    <h1>标签中间的内容</h1>
                    <div>第二块内容</div>
                </div>
                    {/* <h1>标签中间的内容</h1> */}
            </CartChild>
        </div>
    );
}

export default Cart;

子组件

import { withRouter } from 'react-router-dom'
const CartChild = (props) => {
    console.log('CartChild', props)
    return (
        <div>
            CartChild
            <hr/>
            {props.children}
        </div>
    );
}

export default withRouter(CartChild);

相关文章

  • React Hooks && 其它补充知识点

    组件类的缺点 React 的核心是组件。v16.8 版本之前,组件的标准写法是类(class)。下面是一个简单的组...

  • React Hooks

    React Hooks Hooks其实就是有状态的函数式组件。 React Hooks让React的成本降低了很多...

  • react-hooks

    前置 学习面试视频 总结react hooks react-hooks react-hooks为函数组件提供了一些...

  • React Hooks

    前言 React Conf 2018 上 React 提出了关于 React Hooks 的提案,Hooks 作为...

  • 5分钟简单了解React-Hooks

    首先附上官网正文?:React Hooks Hooks are a new addition in React 1...

  • react-hooks

    react-hooks react-hooks 是react16.8以后,react新增的钩子API,目的是增加代...

  • React-hooks API介绍

    react-hooks HOOKS hooks概念在React Conf 2018被提出来,并将在未来的版本中被...

  • React Hooks 入门

    React Hooks 是 React v16.8 版本引入了全新的 API。 React Hooks 基本概念 ...

  • react hooks 源码分析 --- useState

    1. react hooks简介 react hooks 是react 16.8.0 的新增特性,它可以让你在不编...

  • React Hooks的入门简介

    什么是React Hooks? 首先React Hooks是React生态圈里的新特性,它改变了传统react的开...

网友评论

      本文标题:React Hooks && 其它补充知识点

      本文链接:https://www.haomeiwen.com/subject/pbcjrltx.html