目录
一、React中类似于vue中的插槽
二、自定义HOOK
三、插槽和自定义HOOK配合使用
四、useReducer
1.useReducer引入
2.语法:
3.案例:
4.useReducer +useContext实现redux
5.useReducer vs useState(面试)
App.jsx:
import React,{useState} from 'react'
import Box from "./Box.jsx"
import Box2 from "./Box2.jsx"
import useTool1 from './useTool1.jsx'
export default function App() {
let [title,changetitle]=useState("app-title")
let Login=useTool1(123)
return (
app
666
)
}
Box.jsx:
import React,{useEffect} from 'react'
export default function Box(props) {
useEffect(()=>{
console.log(props)
},[])
return (
box-{props.mysrc}--{props.mytitle}
{props.children}
)
}
Box2.jsx
import React from 'react'
export default function Box2() {
return (
Box2
)
}
会显示:
HOOK使用场景:use开头的那些官方提供的HOOK函数,只能在函数组件(返回值是一个模板,且必须有一个根节点)中,或者自定义HOOK的函数中,不能在类或者普通时间函数中使用。
自定义HOOK:就是利用官方提供的HOOK来实现自己的一个具有业务功能的函数,它的特点就是使用后返回一个组件,这个思想就是类组件中的高阶组件
函数组件和自定义HOOK:组件返回模板,HOOK返回组件
App.jsx
import React,{useState} from 'react'
import Box from "./Box.jsx"
import Box2 from "./Box2.jsx"
import useTool1 from './useTool1.jsx'
export default function App() {
let [title,changetitle]=useState("app-title")
let Login=useTool1(123)
return (
app
666
)
}
自定义HOOK:useTool1
import React, { useState, useMemo } from 'react'
function useTool1(id) {
let [flag, setflag] = useState(true)
let isLogin = useMemo(() => {
let res = false//假装用id网络请求后端
setflag(res)
return flag
}, [id])
if (isLogin) {
return function () {
return (
用户名:假数据
)
}
} else {
return function () {
return ()
}
}
}
export default useTool1;
App.jsx
import React from 'react'
import ctx from './Myprovider'
export default function App() {
let Myprovider=ctx()
return (
)
}
Myprovider.jsx
import React,{useState} from 'react'
export default function Myprovider() {
let [msg,setMsg]=useState({msg:"hello"})
function Box(props){
return(
box
{props.children}
)
}
return Box
}
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
App.jsx
import React,{useState,useReducer} from 'react'
export default function App() {
let [count,setCount]=useState(10)
let reducer=(initSate,action)=>{
if(action.type=="MSG"){
initSate.msg=action.value
}
initSate=JSON.parse(JSON.stringify(initSate))
return initSate
}
let [state,dispatch]=useReducer(reducer,{msg:"hello"})
console.log(state)
return (
App
{count}
{state.msg}
)
}
1)例子1
import React from "react"
let context1=React.createContext(null)
export default context1
import { useRef, useEffect, useCallback, useState, useMemo, useReducer } from 'react'
import Mycontext from "./MyContext.jsx"
import Mybox2 from "./Mybox2.jsx"
function Mybox() {
let [state,dispach]=useReducer((state,action)=>{
if(action.type=="msg"){
state.msg= action.value
}
else if(action.type=="age"){
state.age= action.value
}
console.log(state)
return JSON.parse(JSON.stringify(state))
},{msg:"hello",age:18})
// useReducer+useContext ==>做出redux框架的功能(全局数据共享)
//1.给根组件生成一个数据容器(不是用useState生成的,是用useReducer)
//2.把这个数据容器(是一个hook容器)传给子代组件使用,使用的技术是useContext
return (
1
{state.msg}
)
}
export default Mybox
import {useReducer,useContext} from 'react'
import context1 from "./MyContext.jsx"
import MyBox3 from "./MyBox3.jsx"
export default function Mybox2() {
const [state,dispacth] = useContext(context1)
console.log(state);
let fn=()=>{
console.log(6666)
dispacth({type:"msg",value:"1234567"})
}
return (
2
{state.msg}
)
}
import React,{useContext} from 'react'
import context2 from "./MyContext.jsx"
export default function Mybox3() {
let [state,dispacth]=useContext(context2)
return (
3
{state.msg}
)
}
2) 例子2
src/store/index.jsx
import React, { useReducer } from 'react'
import ctx from "./store"
export default function Myredux(props) {
let reducer = (state, action) => {
if (action.type == "MSG") {
state.msg = action.value
}
if (action.type == "TOKEN") {
state.token = action.value
}
state = JSON.parse(JSON.stringify(state))
return state
}
let [store, dispatch] = useReducer(reducer, { msg: "hello", token: "af123123" }, function (arg) {
//可以处理数据 然后返回值作为仓库的初始状态
arg.msg = "123"
return arg
})
return (
{props.children}
)
}
src/store/store.jsx
import React from 'react'
let ctx=React.createContext(null)
export default ctx;
App.jsx
import React,{useContext} from 'react'
import ctx from "./store/store"
import Box from './Box.jsx'
export default function App() {
let [store,dispatch]=useContext(ctx)
console.log(store,66661)
return (
App
{/* {obj.msg}
*/}
App--{store.msg}
)
}
Box.jsx
import React,{useContext} from 'react'
import store from "./store/store"
import Box2 from './Box2.jsx'
export default function Box() {
let [state,dispatch]=useContext(store)
return (
Box--{state.msg}
)
}
Box2.jsx
import React,{useContext} from 'react'
import Store from "./store/store"
export default function Box2() {
let [state,dispatch]=useContext(Store)
return (
Box2--{state.msg}
)
}
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