❝我这个人不喜欢说这么多花里胡哨的 直接上代码,都是个人比较收录,有问题评论区受教了大家 >v<
❞
什么是React Hook
- React 一直都提倡使用函数组件,但是有时候需要使用 state 或者其他一些功能时,只能使用类组件,因为函数组件没有实例,没有生命周期函数,只有类组件才有
- Hooks 是 React 16.8 新增的特性,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
- Hook 不会影响你对 React 概念的理解。 恰恰相反,Hook 为已知的 React 概念提供了更直接的 API:props, state,context,refs 以及生命周期。稍后我们将看到,Hook 还提供了一种更强大的方式来组合他们。
- 凡是 use 开头的React Hook的API
Hook比类好在哪?
不需要this
- 为了保证this的指向正确,我们要经常写这样的代码:
- this.handleClick = this.handleClick.bind(this), - 或者是这样的代码:<button onClick={() => this.handleClick(e)}>。
- 一旦我们不小心忘了绑定this,各种bug就随之而来,很麻烦。
简化类组件的生命周期,使其不这么复杂
- 我们通常希望一个函数只做一件事情,但我们的生命周期钩子函数里通常同时做了很多事情。
- 比如我们需要在componentDidMount中发起ajax请求获取数据,绑定一些事件监听等等。同时,有时候我们还需要在componentDidUpdate做一遍同样的事情。当项目变复杂后,这一块的代码也变得不那么直观。
能在无需修改组件结构的情况下复用状态逻辑(自定义 Hooks )
副作用横切关注点,由useEffect和useLayoutEffect解决,如 ajax 请求、访问原生dom 元素、本地持久化缓存、绑定/解绑事件、添加订阅、设置定时器、记录日志等
解决类组件的状态复用问题
- 你在大型的工作项目中用react,你会发现你的项目中实际上很多react组件冗长且难以复用。尤其是那些写成class的组件,它们本身包含了状态(state),所以复用这类组件就变得很麻烦,官方给出渲染属性(Render Props)和高阶组件两种解决方式。
- 而这两种方式会增加我们代码的层级关系,而HOOK就比较简单
❝我们看看hook复用性
❞
//useHook.jsx
import React, { useState } from "react";
function useHook(item) {
const [status, setStatus] = useState("穿鞋子");
setTimeout(() => {
setStatus("穿好鞋子");
}, item.time);
return (
<h1 key={item.id}>
{status === "穿鞋子"
? `${item.name}正在穿鞋子...`
: `${item.name}已经穿好鞋子了`}
</h1>
);
}
export default useHook;
import React from "react";
import useHook from "./useHook";//哦吼
function Page() {
const data = [
{ id: 1, name: "小A", time: "2000" },
{ id: 2, name: "小B", time: "5000" }
];
return (
<div>
{data.map(item => {
return useHook(item);
})}
</div>
);
}
export default Page;
useState
「useState是react自带的一个hook函数,它的作用就是用来声明状态变量。」
useState这个函数接收的参数是我们的状态初始值(initial state)
它返回了一个数组,
第[0]项是当前当前的状态值,不填为undefiend
第[1]项是可以改变状态值的方法函数。
import { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
// count --> 0
// setCount(count=>count+100)
//count --> 100
- 数组形式
// const [list, setList] = useState([]);
<button onClick={() => {
list.splice(0, 1)
setList([...list])
}}>Push</button>
- 对象形式
const [obj, setObj] = useState({
a:1,b:2
})
<button onClick={() => setObj({
...obj,
b:22222
})}>obj</button>
- 获取setState更新后的值
setNumber(number + 1, () => {
console.log(number);
})
useState注意的细节
useState最好写到函数的起始位置,便于阅读
useState严禁出现在代码块(判断、循环)中
useState返回的函数(数组的第二项),引用不变(节约内存空间)
使用函数改变数据,若数据和之前的数据完全相等不会导致重新渲染,以达到优化效率的目的)
使用函数改变数据,传入的值不会和原来的数据进行合并,而是直接替换。(setState是混合)
const [, forceUpdate] = useState({}); 调用函数 forceUpdate({}) 传入空对象 强制刷新
每次渲染都是独立的闭包,每一次渲染都有它自己的 Props 和 State 每一次渲染都有它自己的事件处理函数
useEffect 副作用钩子
「为函数组件增加了操作副作用的能力。」
「它跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API」
- useEffect 接收一个函数,该函数会在组件渲染到屏幕之后才执行
- 该函数有要求:要么返回一个能清除副作用的函数,要么就不返回任何内容
- 该函数接收一个数组,为依赖值,值有变化立即触发函数
❝useEffect有多种形式 我们来瞅瞅 0.0
❞
- 默认不传参,首次渲染不执行,每次有数据重新渲染时会促发Effect
useEffect(() => {
console.log(123)
});
- 传入一个空数组 , 可以理解为等同于componentDieMount和componentDidUpdate
useEffect(() => {
console.log(123)
}),[];
- 依赖性变化,会触发Effect
- 首次渲染触发一次
- 依赖项变化再次触发
useEffect(() => {
console.log(123)
},[number,number2]);
//number和number2其一变化 即打印123
- 组件卸载时,会执行return清理函数
useEffect(()=>{
let $timer = setInterval(()=>{
setNumber(number=>number+1);
},1000);
return ()=>{ //组件切换的时候 停止定时器
clearInterval($timer)
}
});
useLayoutEffect
useEffect:浏览器渲染完成后,render结束后就运行;
useLayoutEffectHook:Dom一更新就执行,此时还没有render,比effect早一步
useMemo
「对值进行缓存,性能优化」
const [content, setContent] = useState('内容')
const [name, setName] = useState('名称')
// const otherName = changeName(name) //会被其他数据影响
const otherName = useMemo(()=>changeName(name),[name])//只有name更新了才会影响到
function changeName(name) {
console.log('11')
return name + '改变name的方法'
}
return (
<>
<button onClick={() => setName(new Date().getTime())}>name</button>
<button onClick={() => setContent(new Date().getTime())}>content</button>
{content} {otherName}
</>
)
- 使用memo,只有name有改变立马触发changeName进行实时渲染,其他事件不会影响触发该函数
- 如果不使用memo其他人可以通过其他事件触发该函数。
useCallback
「缓存函数,性能优化,只要依赖项没有发生变化,则始终返回之前函数的地址」
const [message, setMessage] = useState('hello world.');
const handleChange = useCallback((value) => {
setMessage(value);
}, []);
useReducer
「如果代码稍微复杂一些,可以不使用useState,用useReducer。」
❝使用过redux或者vuex应该都很熟悉把
❞
- action是改变数据的唯一原因(本质上就是一个对象,action有两个属性) -type:字符串,动作的类型 -payload:任意类型,动作发生后的附加信息
- reducer函数用来改变数据
-接收2个参数
- state:表示当前数据仓库中的数据
- action:描述了如何去改变数据,以及改变数据的一些附加信息 - -
- reducer必须是纯函数,不能有任何副作用
- dispatch用来促触发reducer
- 该函数仅接收一个参数:action
- 该函数会间接去调用reducer,以达到改变数据的目的
import React from 'react';
import { useState, useContext, useReducer } from 'react'
const initialState = {
a: 1,
b: 2,
number: 0
};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { number: state.number + 1 };
case 'decrement':
return { number: state.number - 1 };
default:
throw new Error();
}
}
export default function Test() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
你好
Count: {state.number} a:{state.a} b:{state.b}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
)
useContext
「createContext创建上下文传递数据 useContext接受上下文的数据」
//父组件
import Test from './component/Test'
import React, {createContext} from 'react'
const ctx = React.createContext();
export default function App() {
return (
<div>
<ctx.Provider value="abc">
<Test />
</ctx.Provider>
</div>
)
}
//子组件,
可以将value传入给子组件(孙组件)
function Test() {
const value = useContext(ctx);
return <h1>Test,上下文的值:{value}</h1> //abc
}
使用 useContext+useReducer 可以搭建一个迷你的redux
useRef
「类组件、React 元素用 React.createRef,函数组件使用 useRef」
- useRef 返回一个可变的 ref 对象,其 current 属性被初始化为传入的参数(initialValue)
export default function Counter() {
const childRef = useRef(null);
const inputRef = useRef(null);
useEffect(()=>{
console.log(childRef) //div
},[])
return (
<>
<input ref={inputRef}></input>
<button onClick={() => {
console.log(inputRef.current.value)//input当前输入框的值
}}>点我获取ref</button>
<div ref={childRef}></div>
</>
)
}
自定义hook
「可以理解为HOC」
import React, { useLayoutEffect, useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
function useNumber(){
let [number,setNumber] = useState(0);
useEffect(()=>{
setInterval(()=>{
setNumber(number=>number+1);
},1000);
},[]);
return [number,setNumber];
}
// 每个组件调用同一个 hook,只是复用 hook 的状态逻辑,并不会共用一个状态
function Counter1(){
let [number,setNumber] = useNumber();
return (
<div><button onClick={()=>{
setNumber(number+1)
}}>{number}</button></div>
)
}
function Counter2(){
let [number,setNumber] = useNumber();
return (
<div><button onClick={()=>{
setNumber(number+1)
}}>{number}</button></div>
)
}
ReactDOM.render(<><Counter1 /><Counter2 /></>, document.getElementById('root'));
上一个老八蜜汁小汉堡 useXState
「」
const useXState = (initState) => {
const [state, setState] = useState(initState)
let isUpdate = useRef()
const setXState = (state, cb) => {
isUpdate.current = cb
setState(state)
}
useEffect(() => {
if (isUpdate.current) {
isUpdate.current();//执行cb !!!
isUpdate.current = null;
}
}, [state]);
return [state, setXState]
}
- 之后我们修改对象时就可以使用回调打印最新值了 (useState的话会报错,会教你用useEffect监听)
const [val, setVal] = useXState({ a: 1, b: 2 });
return(
<>
{val.a}
<button onClick={() => {
setVal({...val,a:2333},()=>{
console.log(val)
} )
}}>AAA</button>
</>
)
❝useHook 就到这里了 x.x 前端小白一个 希望大佬发现问题能大胆的说我会吸取其精