目录
Hook 简介
Class方式创建组件
State Hook
Effect Hook
当 React 渲染组件时,会保存已使用的 effect,并在更新完 DOM 后执行它。这个过程在每次渲染时都会发生,包括首次渲染。
Hook规则与自定义Hook
其他常用Hook
useContext
ReactRouter Hook
Redux Hook
Redux-toolkit(RTK)
Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用
官网https://zh-hans.reactjs.org/docs/hooks-intro.html
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
import React, { Component } from 'react'
export default class ClassDemo extends Component {
constructor(){
super();
this.state = {
message:"Class方式创建组件!"
}
}
clickHandle = () =>{
this.setState({
message:"新的Class值"
})
}
render() {
return (
{ this.state.message }
)
}
}
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 (
{message}-{ count}
)
}
export default StateHook
useState
类似 class 组件的 this.setState,
唯一的参数就是初始 state,但是不同于 this.state,
这里的 state 不一定要是一个对象,这个初始 state 参数只有在第一次渲染时会被用到,useState
不会把新旧state 进行合并
在函数组件中,我们没有 this
,所以我们不能分配或读取 this.state
。一般来说,在函数退出后变量就会”消失”,而 state 中的变量会被 React 保留。
useState
方法的返回值是什么? 返回值为:当前 state 以及更新 state 的函数。
useEffect
做了什么? 告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。
为什么在组件内部调用 useEffect
? 将 useEffect
放在组件内部让我们可以在 effect 中直接访问 count
state 变量(或其他 props)。它已经保存在函数作用域中。Hook 使用了 JavaScript 的闭包机制
useEffect
会在每次渲染后都执行吗? 是的,默认情况下,它在第一次渲染之后和每次更新之后都会执行。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 (
Effect Hook
num={num}
)
}
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 (
Effect Hook 副作用
Count={count}
)
}
UseCustomHook.jsx
import React from 'react'
import useMousePosition from "./useMousePosition"
export default function UseCustomHook() {
const position = useMousePosition();
return (
自定义Hook
x={position.x},y={position.y}
)
}
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
}
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 (
refHook
文本信息
)
}
useContext
const value = useContext(MyContext);
接收一个 context 对象(React.createContext
的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的
的 value
prop 决定。
当组件上层最近的
更新时,该 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 (
Main
)
}
ChildPage.jsx
import React,{useContext} from 'react'
import {MyContext} from "./MainPage"
export default function ChildPage() {
const value = useContext(MyContext)
return (
Child
{ value }
)
}
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 (
Home
)
}
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 (
About
List
)
}
useRouteMatch 尝试以与
相同的方式匹配当前URL。它主要用于访问匹配数据,而无需实际渲染
之前:
import { Route } from "react-router-dom";
function BlogPost() {
return (
{
// Do whatever you want with the match...
return ;
}}
/>
);
}
现在:
import { useRouteMatch } from "react-router-dom";
function BlogPost() {
let match = useRouteMatch("/blog/:slug");
// Do whatever you want with the match...
return ;
}
useRouteMatch 钩子也可以:
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 (
About
List
)
}
List.jsx
import React from 'react'
import { useParams } from 'react-router-dom'
export default function List() {
const params = useParams()
console.log(params)
return (
列表
List=>id={params.id}
)
}
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 (
用户中心
)
}
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(
Home |
About
}>
}>
}>
);
组件可以通过useSelector
访问store中释放的state数据
使用useDispatch hook 能得到 redux store 的 dispatch 方法引用
,通常用于“手动” dispatch action 。
之前在使用 connect 的时候,我们通常使用mapDispatchToProps
和actionCreator
封装一下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(
Home |
About
}>
}>
}>
);
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 (
//
// About
//
// List
//
// count={ props.count }
//
// )
// }
// 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 (
About
List
Count={count}
)
}
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(简称:”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 (
Home
Count={ count }
)
}
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(
,
document.getElementById('root')
);
React基础到这就告一段落了....后期会做一些React项目,不至于让学的知识付诸东流...