React---Hook(useReducer,自定义Hook)

49 阅读4分钟

useReducer

1.useReducer用最简单的话来说就是允许我们在函数组件里面像使用redux一样通过reducer和action来管理我们组件状态的变换

语法:

const [state, dispatch] = useReducer(reducer, initialArg, init?)
                                     
//useReducer和useState类似,都是用来管理组件状态的,只不过和useState的setState不一样的是,useReducer返回的dispatch函数是用来触发某些改变state的action而不是直接设置state的值,至于不同的action如何产生新的state的值则在reducer里面定义。//useReducer接收的三个参数分别是:
                                     
//reducer: 这是一个函数,它的签名是(currentState, action) => newState,从它的函数签名可以看出它会接收当前的state和当前dispatch的action为参数,然后返回下一个state,也就是说它负责状态转换的工作。
                                     
//initialArg:如果调用者没有提供第三个init参数,这个参数代表的是这个reducer的初始状态,如果init参数有被指定的话,initialArg会被作为参数传进init函数来生成初始状态。
                                     
//init: 这是一个用来生成初始状态的函数,它的函数签名是(initialArg) => initialState,从它的函数签名可以看出它会接收useReducer的第二个参数initialArg作为参数,并生成一个初始状态initialState

案例:

import React, { useState, useReducer } from 'react'let todoId = 1const reducer = (currentState, action) => {
  switch(action.type) {
    case 'add':
      return [...currentState, {id: todoId++, text: action.text}]
    case 'delete':
      return currentState.filter(({ id }) => action.id !== id)
    default:
      throw new Error('Unsupported action type')
  }
}
​
const Todo = ({ id, text, onDelete }) => {
  return (
    <div>
      {text}
      <button
        onClick={() => onDelete(id)}
      >
        remove
      </button>
    </div>
  )
}
​
const App = () => {
  const [todos, dispatch] = useReducer(reducer, [])
  const [text, setText] = useState('')
​
  return (
    <>
      {
        todos.map(({ id, text }) => {
          return (
            <Todo
              text={text}
              key={id}
              id={id}
              onDelete={id => {
                dispatch({ type: 'delete', id })
              }}
            />
          )
        })
      }
      <input onChange={event => setText(event.target.value)} />
      <button
        onClick={() => {
          dispatch({ type: 'add', text })
          setText('')
        }}
      >
        add todo
      </button>
    </>
  )
}
​
ReactDOM.render(<App />, document.getElementById('root'))
​

2.useReducer +useContext实现redux

2.1 先创建一个上下文组件

import React from "react";
let ctx = React.createContext(null);
export default ctx;

2.2 自己用useReducer构建一个类似于redux的仓库,需要引入这个上下文组件让他成为提供者,将设计好的值和方法转给其他组件,这样所有组件都可以去使用和操作这个组件中的值

import React, { useReducer } from "react";
import ctx from "./store";

export default function Index(props) {
  let reducer = (oldvalue, action) => {
    if (action.type == "NAME") {
      oldvalue.name = action.name;
    }
    oldvalue = JSON.parse(JSON.stringify(oldvalue));

    return oldvalue;
  };
  let [state, dispatch] = useReducer(reducer, { name: "dsl" });
  return (
    <ctx.Provider value={[state, dispatch]}>{props.children}</ctx.Provider>
  );
}

2.3 在index.js文件我们需要将上下文组件引入,然后将上下文组件设为根组件,将App组件插入到上下文组件的插槽中

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App'
import Ctx from './store/index'
const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <Ctx>
    <App></App>
  </Ctx>  
);

2.4在组件中使用,需要用useContext去收上下文组件传过来的值和方法.

//App组件
import React, { useContext } from "react";
import ctx from "./store/store";
import Box from "./Box";

export default function App() {
  let [state, dispatch] = useContext(ctx);

  return (
    <div>
      {state.name}
      <Box></Box>
    </div>
  );
}

//Box组件
import React, { useContext } from "react";
import ctx from "./store/store";
export default function Box() {
  let [state, dispatch] = useContext(ctx);
  let fn = () => {
    dispatch({
      type: "NAME",
      name: "zwq",
    });
  };
  return (
    <div>
      <br />
      <p>BOX</p>
      {state.name}
      <button onClick={fn}>改变名字</button>
    </div>
  );
}

3.useReducer vs useState(面试)

useReducer和useState都可以用来管理组件的状态,它们之间最大的区别就是,useReducer将状态和状态的变化统一管理在reducer函数里面,这样对于一些复杂的状态管理会十分方便我们debug,因为它对状态的改变是封闭的。而由于useState返回的setState可以直接在任意地方设置我们状态的值,当我们组件的状态转换逻辑十分复杂时,它将很难debug,因为它是开放的状态管理。总体的来说,在useReducer和useState如何进行选择的问题上我们可以参考以下这些原则:

  • useState情况使用

    - state的值是JS原始数据类型,如number, string和boolean等
    - state的转换逻辑十分简单
    - 组件内不同的状态是没有关联的,它们可以使用多个独立的useState来单独管理
  • useReducer情况使用

    - state的值是object或者array
    - state的转换逻辑十分复杂, 需要使用reducer函数来统一管理
    - 组件内多个state互相关联,改变一个状态时也需要改变另一个,放在同一个state内使用reducer来统一管理
    - 状态定义在父级组件,不过需要在深层次嵌套的子组件中使用和改变父组件的状态,可以同时使用useReducer和useContext两个hook,将dispatch方法放进context里面来避免组件的props drilling
    - 如果你希望你的状态管理是可预测的和可维护的,请useReducer
    - 如果你希望你的状态变化可以被测试,请使用useReducer

    自定义Hook

1.HOOk使用场景:use开头的那些官方提供的HOOK函数 只能在函数组件内部或者是自定义HOOK函数中使用,不能在类或者普通事件函数中使用

2.自定义HOOK:就是利用官网提供的HOOK来实现自己的一个具有业务功能的函数.它的特点就是使用后返回一个组件,这个思想就是类组件中的高阶组件

import React,{useState,useMemo} from 'react'
function useTool1(id){ 
    let [flag,setflag]= useState(true)    
    // setflag(res)
   let isLogin=useMemo(()=>{
        let res=false//假装用id网络请求后端
        setflag(res)
        return flag
    },[id])

    if(isLogin){
        return function(){
            return (<div>
                 用户名:假数据
            </div>)
          }
    }else{
        return function(){
            return (<div>
                 <a href="#">登录</a>
            </div>)
          }
    }
   
}
export default useTool1;