前言
React Hook是React-16.8开始出现的新特性,它可以让你在不编写class的情况下使用state及其它的React特性,Hook是一个特殊的函数表现。
React Hook 解决了什么问题?
- 避免疯狂式嵌套,可读性提高。
- 函数式组件,比class语法更加已读和理解。
- class组件钩子函数太多太复杂。
- 解决HOC和render props的缺点。
- UI和逻辑更方便解耦。
- 解决组件之间的副作用。
useState
在函数组件中,可以用来管理组件的状态。
import React, { useEffect, useState } from "react";
function ComponentApp(){
const [count,setCount] = useState(0);
return (
<>
<div onClick={()=>setCount(count+1)}>+</div>
{count}
<div onClick={()=>setCount((pv)=>pv-1)}>-</div>
</>
)
};
说明:
- 只能在函数最顶层使用hook,不要再循环、条件判断或子函数中调用。
- 只能在react的函数组件中调用hook,不能是普通函数,也不能是class组件。
useEffect
接收一个包含命令式,且可能有副作用代码的函数。
import React, { useEffect, useState } from "react";
function ComponentApp(){
const [count,setCount] = useState(0);
useEffect(()=>{
// 这里执行相当于react class中DidMount和DidUpdate钩子。
console.log("count状态更新了,值:",count);
return ()=>{
// unMount
console.log("卸载状态");
}
},[count]);
return (
<>
{count}
<div onClick={()=> setCount(count+1)}>+</div>
</>
)
};
功能说明:
- 支持在组件内完成挂载、状态更细和卸载的能力。
- 第一个参数是一个callback,可以用来做一些副作用,比如异步请求,修改外部参数等行为。
- useEffect的第二个参数source是一个触发条件,类型为数组,如果数组中观测的值发生变化才会触发useEffect的callback回调。
- 没有传入参数时,默认每次render更改时都会重新执行回调。
- 传递一个[],只有在组件初始化或销毁的时候才会触发;
- 传入[source],在依赖state发生改变时,才重新执行该回调。
- 在组件里useEffect可以编写多个,会按照顺序进行执行。
useLayoutEffect
与useEffect函数类似,但会在DOM修改后同步触发执行,并且比useEffect先执行,会阻塞浏览器的函数渲染。
useEffect执行顺序:组件更新挂载完成,DOM绘制完成,执行useEffect回调。 useLayoutEffect执行顺序:组件更新挂载完成,执行useLayoutEffect回调,DOM绘制完成。
import React, { useEffect,useLayoutEffect, useState } from "react";
function ComponentApp(){
const [count,setCount] = useState(0);
useEffect(()=>{
// 模拟耗时操作
const passedTime = (new Date()).getTime();
const now = (new Date()).getTime();
while(now - oassedTime < 1000){};
if(!count){
setCount(1+Math.random()*1000)
}
},[count]);
useLayoutEffect(()=>{
// count状态更新了, DOM再进行更新。
const passedTime = (new Date()).getTime();
const now = (new Date()).getTime();
while(now - oassedTime < 1000){};
if(!count){
setCount(1+Math.random()*1000)
}
return ()=>{
// unMount
console.log("卸载状态");
}
},[count]);
return (
<>
{count}
<div onClick={()=> setCount(count+1)}>+</div>
</>
)
};
useMemo
提升性能,给子组件传递状态时绑定这个函数,可以避免组件更新时,子组件进行不必要的更新。
useCallback
通useMemo类似,给子组件传递函数时绑定这个函数,可以避免组件更新时,子组件进行不必要的更新。
useContext
组件之间的通信,组件深入传值时,props不如useContext方便。
有两种方式: 首先通过export const MyContext = React.createContext({data:{}}),暴露要传递的状态。
- 配合<MyContext.Provider data={{val:"test"}}>进行传递,在子组件中使用。
- 在子组件中import {MyContext} from "./MyContext",在方法里调用 const obj = useContext(MyContext);
如果不想使用.Provider作为提供者传递数据,则可以在子组件中import之后,直接使用useContext进行操作。
// MyContext.js
import React from "react";
const MyContext = React.createContext({
data: {
val: "test"
}
});
export MyContext;
// hook component app
import { MyContext } from "./MyContext";
import React, { useEffect, useContext, useState } from "react";
function ComponentApp(props={}){
const MyContext = useContext(MyContext);
useEffect(()=>{
},[]);
return (
<div>{MyContext.data.val}</div>
);
};
- MyContext.js 每次返回的都是一个新的对象。
useReducer
用于集中管理状态,可做全局或局部的状态管理。
全局状态管理要配合 <MyContext.Provider>、React.createContext、useContext 进行。
// stores.js
export default {
state: {
name: "小明",
age: 16,
gender: "男"
}
}
// reducer.js
export function reduceCount(state,action){
switch(action.type){
case "add":
return {...state, count: state.count+1}
break;
case "sub":
return {...state, count: state.count-1}
break;
default:
return state;
}
};
// myContext.js
export function MyContext(){
return React.createContext({
data: {
val: "test"
}
});
};
// App.jsx
import React from "react";
function App(){
return (
<>
<ChildComponent />
</>
)
};
export default App;
// ChildComponent.jsx
import React, { useEffect, useContext, useReducer, useState } from "react";
function ChildComponent(props={}){
const [state,dispatch] = useReducer();
return (
)
};
useRef
用于在函数组件中获取DOM元素。
import React, { useEffect, useRef } from "react";
function ComponentApp(){
const inputRef = useRef(null);
useEffect(()=>{
let val = inputRef.current.value;
console.log(val);
return ()=>{};
},[]);
return (
<input type="text" ref={inputRef} />
)
};
功能:
- 返回一个可变的 ref 对象,该对象只有个 current 属性,初始值为传入的参数( initialValue )。
- 返回的 ref 对象在组件的整个生命周期内保持不变
- 当更新 current 值时并不会 re-render ,这是与 useState 不同的地方
- 更新 useRef 是 side effect (副作用),所以一般写在 useEffect 或 event handler 里
- useRef 类似于类组件的 this
useImperativeHandle
功能:
- 可以让父组件操作子组件的方法
forwardRef
功能:
- 可以在组件引用时写ref,将ref透传到子组件内。
Hook的优点
- 函数式组件
Hooks只能在函数组件中使用,直接就强制避免了类结构对开发者、代码分析器等角色造成的困惑性。 - 组件层级变浅,提高可维护性,有利于编写复杂组件,有利于改善
嵌套地狱问题。 - 极简的代码风格,可读性高,将特定的代码整合,方便后期阅读。
参考文章
原文链接
您觉得还不错的话,感谢点赞+关注~