ReactHook新特性

目录

Hook 简介

 Class方式创建组件

 State Hook

Effect Hook 

当 React 渲染组件时,会保存已使用的 effect,并在更新完 DOM 后执行它。这个过程在每次渲染时都会发生,包括首次渲染。

Hook规则与自定义Hook

其他常用Hook

 useContext

ReactRouter Hook

 Redux Hook

 Redux-toolkit(RTK)


Hook 简介

Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用

官网https://zh-hans.reactjs.org/docs/hooks-intro.html

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

  1. 没有破坏性改动
    1. 完全可选的
    2. 100% 向后兼容的。 Hook 不包含任何破坏性改动。
    3. 现在可用。 Hook 已发布于 v16.8.0。
  2. 动机(为什么要在 React 中引入 Hook )
    1. 在组件之间复用状态逻辑很难
    2. 复杂组件变得难以理解
    3. 难以理解的 class

 Class方式创建组件

import React, { Component } from 'react'

export default class ClassDemo extends Component {

    constructor(){
        super();
        this.state = {
            message:"Class方式创建组件!"
        }
    }

    clickHandle = () =>{
        this.setState({
            message:"新的Class值"
        })
    }

    render() {
        return (
            <div>
                <h3>{ this.state.message }</h3>
                <button onClick={ this.clickHandle }>修改文本</button>
            </div>
        )
    }
}

 State Hook

                            StateHook.jsx
import { useState } from 'react'

function StateHook() {
    /**
     * State Hook形式
     */
    const [message, setMessage] = useState("StateHookDemo!")
    const [count, setCount] = useState(0)
    function clickHandle() {
        setMessage('new StateHookDemo')
    }
    function addHandle() {
        setCount(count + 1)
    }
    function minHandle() {
        setCount(count - 1)
    }
    return (
        <div>
            {message}-{ count}
            <button onClick={clickHandle}>修改状态</button>
            <button onClick={addHandle}>增加</button>
            <button onClick={minHandle}>减少</button>
        </div>
    )
}
export default StateHook

 useState 类似 class 组件的 this.setState,唯一的参数就是初始 state,但是不同于 this.state,这里的 state 不一定要是一个对象,这个初始 state 参数只有在第一次渲染时会被用到,useState 不会把新旧state 进行合并

在函数组件中,我们没有 this,所以我们不能分配或读取 this.state。一般来说,在函数退出后变量就会”消失”,而 state 中的变量会被 React 保留。

useState 方法的返回值是什么? 返回值为:当前 state 以及更新 state 的函数。

Effect Hook 

  1. 基本使用  
  2. 分离业务:因为可以增加多个类似生命周期函数的方法:useEffect
    重点:如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

 useEffect 做了什么? 告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。

 为什么在组件内部调用 useEffect 将 useEffect 放在组件内部让我们可以在 effect 中直接访问 count state 变量(或其他 props)。它已经保存在函数作用域中。Hook 使用了 JavaScript 的闭包机制

useEffect 会在每次渲染后都执行吗? 是的,默认情况下,它在第一次渲染之后每次更新之后都会执行。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。

当 React 渲染组件时,会保存已使用的 effect,并在更新完 DOM 后执行它。这个过程在每次渲染时都会发生,包括首次渲染。

 每次我们重新渲染,都会生成新的 effect,替换掉之前的

 某种意义上讲,effect 更像是渲染结果的一部分 —— 每个 effect “属于”一次特定的渲染

useEffect 可以在组件渲染后实现各种不同的副作用。有些副作用可能需要清除,所以需要返回一个函数。

 为什么要在 effect 中返回一个函数? 这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起。它们都属于 effect 的一部分

React 何时清除 effect? React 会在组件卸载的时候执行清除操作。正如之前学到的,effect 在每次渲染的时候都会执行。这就是为什么 React 在执行当前 effect 之前对上一个 effect 进行清除

我们可以在一个组件中多次调用 useState 和 useEffect,它们是完全独立的。

                                EffectHookDemo.jsx
import React, { useState, useEffect } from 'react'

export default function EffectHookDemo() {
    //状态
    const [count, setCount] = useState(0)
    const [num, setNum] = useState(0)
    /**
     * componentDidMount
     * componentDidUpdate
     */
    useEffect(() => {
        console.log('自动执行')
        document.title = `title=${count}`
    })
    /**
     * []:监听那个状态的修改,如果什么都不写代表不监听任何状态,就相当于componentDidMount
     * [num]:当num发生改变的时候,他就会触发,相当于componentDidUpdate
     */
    useEffect(() => {
        console.log("自动执行吧")
    }, [num])
    function addHandle() {
        setCount(count + 1)
    }

    function addNumHandle() {
        setNum(num + 1)
    }
    return (
        <div>
            <h3>Effect Hook</h3>
            <p>num={num}</p>
            <button onClick={addNumHandle}>Num增加</button>
            <button onClick={addHandle}>增加</button>
        </div>
    )
}
                                EffectHookDemo2.jsx
import React, { useState, useEffect } from 'react'
const MyAPI = {
    count: 0,
    subScribe(cb) {
        this.intervalId = setInterval(() => {
            this.count += 1
            cb(this.count)
        }, 1000)
    },
    unSubScribe() {
        clearInterval(this.intervalId)
        this.reset();
    },
    reset() {
        this.count = 0;
    }
}
/**
 * 在compnentWillUnmount:清除定时器
 */
export default function EffectHookDemo2() {
    const [count, setCount] = useState(0)
    useEffect(() => {
        MyAPI.subScribe(count => {
            setCount(count)
            console.log(count)
        })
        // 清除副作用,return函数相当于compnentWillUnmount生命周期函数
        return function () {
            MyAPI.unSubScribe()
        }
    }, [])

    return (
        <div>
            <h3>Effect Hook 副作用</h3>
            <p>Count={count}</p>
        </div>
    )
}

Hook规则与自定义Hook

  1. Hook规则
    1. 只在最顶层使用 Hook(不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。)
    2. 只在 React 函数中调用 Hook(不要在普通的 JavaScript 函数中调用 Hook
  2. 自定义Hook
    自定义Hook,命名规则"use"开头
    应用场景:提取公共业务,可以多次调用
                        UseCustomHook.jsx
import React from 'react'
import useMousePosition from "./useMousePosition"
export default function UseCustomHook() {
    const position = useMousePosition();
    return (
        <div>
            <h3>自定义Hook</h3>
            <p>x={position.x},y={position.y}</p>
        </div>
    )
}
                      useMousePosition.jsx
import React, { useState, useEffect } from 'react'
export default function useMousePosition() {
    const [position, setPosition] = useState({ x: 0, y: 0 })
    useEffect(() => {
        //updateMouse
        const updateMouse = e => {
            setPosition({
                x: e.clientX,
                y: e.clientY
            })
        }
        document.addEventListener('mousemove', updateMouse)
        return () => {
            document.removeEventListener('mousemove', updateMouse)
        }
    })

    return position
}

其他常用Hook

  1. useRef
  2. useContext
                              RefHookDemo.jsx
import React, { useRef } from 'react'
// refHook 用来获取原生元素
export default function RefHookDemo() {
    const text = useRef(null)
    function clickHandle() {
        console.log(text.current.innerHTML = 'hello')
    }
    return (
        <div>
            <h3>refHook</h3>
            <p ref={text}>文本信息</p>
            <button onClick={clickHandle}>获取文本</button>
        </div>
    )
}

 useContext

const value = useContext(MyContext);

 接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。

 当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。即使祖先使用 React.memo 或 shouldComponentUpdate,也会在组件本身使用 useContext 时重新渲染。

别忘记 useContext 的参数必须是 context 对象本身

  • 正确: useContext(MyContext)
  • 错误: useContext(MyContext.Consumer)
  • 错误: useContext(MyContext.Provider)

 调用了 useContext 的组件总会在 context 值变化时重新渲染

                            MainPage.jsx
import React from 'react'
import ChildPage from "./ChildPage"
// Context
export const MyContext = React.createContext()
export default function MainPage() {
    return (
        <div>
            <h3>Main</h3>
            <MyContext.Provider value='from_MainPage_data'>
                <ChildPage />
            </MyContext.Provider>
        </div>
    )
}
                            ChildPage.jsx
import React,{useContext} from 'react'
import {MyContext} from "./MainPage"
export default function ChildPage() {
    const value = useContext(MyContext)
    return (
        <div>
            <h3>Child</h3>
            <p>{ value }</p>
        </div>
    )
}

ReactRouter Hook

  1. useHistory(V5)   V6:useNavigate
  2. useLocation
  3. useParams
  4. useRouteMatch

useNavigate  用来跳转并传值

 react useLocation做什么用的

// 跳转传值
import { useNavigate } from 'react-router-dom'
const navigate = useNavigate();
 function clickCityHandle(city) {
    navigate('/home', { state: { name: 'zhou' } })
  }

// 接收跳转传过来的值
import { useLocation } from 'react-router-dom'
const location = useLocation()
const { state } = location
console.log(location, state);
                Home.jsx
import React from 'react'
import StateHook from "../HookDemo/StateHook"
import EffectHookDemo from "../EffectHook/EffectHookDemo"
import EffectHookDemo2 from "../EffectHook/EffectHookDemo2"
import UseCustomHook from "../CustomHook/UseCustomHook"
import RefHookDemo from "../OtherHook/RefHookDemo"
import MainPage from "../OtherHook/compose/MainPage"
import { useLocation } from 'react-router-dom'
export default function Home() {
    const location = useLocation()
    const { state } = location
    console.log('location111', location, state);
    // location111 {pathname: '/home', search: '', hash: '', state: {…}, key: '0irto862'}hash: ""key: "0irto862"pathname: "/home"search: ""state: {data: 'from_about-navigate_data'}[[Prototype]]: Object {data: 'from_about-navigate_data'}
    return (
        <div>
            <h3>Home</h3>
            <StateHook />
            <EffectHookDemo />
            <EffectHookDemo2 />
            <UseCustomHook />
            <RefHookDemo />
            <MainPage />
        </div>
    )
}

                About.jsx
import React from 'react'
import User from "./User"
import { useNavigate, Link } from "react-router-dom"
export default function About(props) {
    const navigate = useNavigate()
    function clickHandle() {
        navigate('/home',{state:{data:'from_about-navigate_data'}})
    }
    return (
        <div>
            <h3>About</h3>
            <button onClick={clickHandle}>按钮</button>
            <Link to="/list/10001">List</Link>
            <User />
        </div>
    )
}

useRouteMatch 尝试以与<Route>相同的方式匹配当前URL。它主要用于访问匹配数据,而无需实际渲染<Route>

之前:
import { Route } from "react-router-dom";
function BlogPost() {
  return (
    <Route
      path="/blog/:slug"
      render={({ match }) => {
        // Do whatever you want with the match...
        return <div />;
      }}
    />
  );
}
现在:
import { useRouteMatch } from "react-router-dom";

function BlogPost() {
  let match = useRouteMatch("/blog/:slug");

  // Do whatever you want with the match...
  return <div />;
}

 useRouteMatch 钩子也可以:

  • 不接受任何参数并返回当前<Route>
  • 接受单个参数,与matchPath的props参数相同。它可以是一个字符串形式的路径名,也可以是一个带有匹配道具的对象,像这样:
  • const match = useRouteMatch({
      path: "/BLOG/:slug/",
      strict: true,
      sensitive: true
    });
                About.jsx
import React from 'react'
import User from "./User"
import { useNavigate, Link } from "react-router-dom"
export default function About(props) {
    const navigate = useNavigate()
    function clickHandle() {
        navigate('/home')
    }
    return (
        <div>
            <h3>About</h3>
            <button onClick={clickHandle}>按钮</button>
            <Link to="/list/10001">List</Link>
            <User />
        </div>
    )
}

                List.jsx
import React from 'react'
import { useParams } from 'react-router-dom'
export default function List() {
    const params = useParams()
    console.log(params)
    return (
        <div>
            <h3>列表</h3>
            <p>List=>id={params.id}</p>
        </div>
    )
}

                    User.jsx
import React from 'react'
import { useNavigate } from 'react-router-dom'
// v6开始 useNavigate取代了原先版本中的useHistory 
export default function User(props) {
    // const history = useHistory()
    const navigate = useNavigate()
    function clickHandle() {
        //https://stackoom.com/en/question/4kYPb
        //https://blog.csdn.net/weixin_44058725/article/details/126622767
        // navigate.push('/home')Uncaught TypeError: navigate.push is not a function
        navigate('/home')//If you are using React Router v6: use navigate("/") instead of navigate.push("/")
    }
    return (
        <div>
            <h3>用户中心</h3>
            <button onClick={clickHandle}>按钮toHome</button>
        </div>
    )
}

                        src -> index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { HashRouter, Routes, Route, Link } from 'react-router-dom'
import Home from "./views/Home"
import About from "./views/About"
import List from "./views/List"
const root = ReactDOM.createRoot(document.getElementById('root'));
        root.render(
                  <React.StrictMode>
                    <HashRouter>
                      <div>
                        <Link to="/home">Home</Link> |
                        <Link to="/about">About</Link>
                      </div>
                      <Routes>
                        <Route path='/home' element={<Home />}></Route>
                        <Route path='/about' element={<About />}></Route>
                        <Route path='/list/:id' element={<List />}></Route>
                      </Routes>
                    </HashRouter>
                  </React.StrictMode>
                );
                

 Redux Hook

  1. useSelector
  2. useDispatch
  3. useStore

 组件可以通过useSelector访问store中释放的state数据

使用useDispatch hook 能得到 redux store 的 dispatch 方法引用,通常用于“手动” dispatch action 。之前在使用 connect 的时候,我们通常使用mapDispatchToPropsactionCreator封装一下dispatch action的过程,然而使用 useDispatch()的时候却需要“手动”调用 dispatch()方法。

useStore() 这个 Hook 直接获取到了 Redux store 的引用

                        src -> index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { HashRouter, Routes, Route, Link } from 'react-router-dom'
import Home from "./views/Home"
import About from "./views/About"
import List from "./views/List"
import { Provider } from 'react-redux'
import store from './redux/store'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <HashRouter>
        <div>
          <Link to="/home">Home</Link> |
        <Link to="/about">About</Link>
        </div>
        <Routes>
          <Route path='/home' element={<Home />}></Route>
          <Route path='/about' element={<About />}></Route>
          <Route path='/list/:id' element={<List />}></Route>
        </Routes>
      </HashRouter>
    </Provider>
  </React.StrictMode>
);



                        src-> views -> About.jsx
// import React from 'react'
// import { useNavigate,Link } from "react-router-dom"
// import User from "./User"
// import { connect } from "react-redux"

// function About(props) {

//     const navigate = useNavigate();

//     function clickHandle(){
    // navigate('/home', { state: { data: 'from_about-navigate_data' } })
//     }

//     return (
//         <div>
//             <h3>About</h3>
//             <button onClick={ clickHandle }>按钮</button>
//             <Link to="/list/10001">List</Link>
//             <User />
//             <p>count={ props.count }</p>
//         </div>
//     )
// }

// const mapStateToProps = state =>{
//     return{
//         count:state
//     }
// }

// export default  connect(mapStateToProps)(About)

import React from 'react'
import User from "./User"
import { useNavigate, Link } from "react-router-dom"
import * as countActions from '../redux/actions/count'
import { useSelector, useDispatch, useStore } from 'react-redux'
export default function About(props) {
    const navigate = useNavigate()
    const count = useSelector(state => state)
    const dispatch = useDispatch()
    const store = useStore()
    console.log(store.getState())
    function clickHandle() {
        navigate('/home', { state: { data: 'from_about-navigate_data' } })
    }
    function clickAddHandle() {
        dispatch(countActions.add())
    }
    return (
        <div>
            <h3>About</h3>
            <button onClick={clickHandle}>按钮</button>
            <Link to="/list/10001">List</Link>
            <User />
            <p>Count={count}</p>
            <button onClick={clickAddHandle}>增加</button>
        </div>
    )
}


                    src->redux->actions->count.js
export function add() {
    return {
        type: 'ADD'
    }
}
export function min() {
    return {
        type: "DEL"
    }
}


                    src->redux->reducers->count.js
const initState = 0
export default function count(state = initState, action) {
    switch (action.type) {
        case 'ADD':
            return state + 1
        case 'DEL':
            return state - 1
        default:
            return state
    }
}


                        src->redux->store->index.js
import {createStore} from 'redux'
import count from '../reducers/count'

const store = createStore(count)
export default store

 Redux-toolkit(RTK)

 Redux-toolkit(简称:”RTX“是官方推荐的编写Redux逻辑的方法,它包裹着Redux核心,简化了大多数Redux任务,使编写Redux应用程序更容易。它包括几个实用程序功能,这些功能可以简化最常见场景下的 Redux 开发,包括配置 store、定义 reducer,不可变的更新逻辑、甚至可以立即创建整个状态的 “切片 slice”,而无需手动编写任何 action creator 或者 action type。它还自带了一些最常用的 Redux 插件,例如用于异步逻辑 Redux Thunk,用于编写选择器 selector 的函数 Reselect ,你都可以立刻使用。

安装依赖:
   cnpm install @reduxjs/toolkit react-redux

                    src -> redux -> counterSlice.js
import { createSlice } from "@reduxjs/toolkit"
// 创建reducers
export const counterSlice = createSlice({
    name:"counter",  // 标示
    initialState:{
        value:0
    },
    reducers:{
        increment:state =>{
            state.value += 1
        },
        decrement:state =>{
            state.value -= 1;
        }
    }
})
// actions
export const { increment,decrement } = counterSlice.actions
// 导出 reducer
export default counterSlice.reducer


                    src -> redux -> store.js
import { configureStore } from "@reduxjs/toolkit"
import counterReducer from "./counterSlice"
export default configureStore({
    reducer:{
        counter:counterReducer
    }
})


                    src -> views -> Home.jsx
import React from 'react'
import { useSelector,useDispatch } from "react-redux"
import { increment,decrement } from "../redux/counterSlice"
export default function Home() {
    const count= useSelector(state => state.counter.value)
    const dispatch = useDispatch();
    function clickAddHandle(){
        dispatch(increment())
    }
    function clickMinHandle(){
        dispatch(decrement())
    }
    return (
        <div>
            <h3>Home</h3>
            <p>Count={ count }</p>
            <button onClick={ clickAddHandle }>增加</button>
            <button onClick={ clickMinHandle }>减少</button>
        </div>
    )
}


                src -> index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from "react-redux"
import store from "./redux/store"
import Home from "./views/Home"
ReactDOM.render(
  <React.StrictMode>
    <Provider store={ store }>
      <div>
        <Home />
      </div>
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

React基础到这就告一段落了....后期会做一些React项目,不至于让学的知识付诸东流...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值