useState
useState 主要用于给组件添加状态变量。
语法
// 变量名用于获取状态变量值,set方法用于设置更新状态变量的值
const [变量名, set变量名] = useState(变量初始化值);
基本使用
function App() {
console.log("App render");
const [count, setCount] = useState(0);
return (
<div className="App">
<button onClick={() => setCount(count + 1)}>
count is {count}
</button>
</div>
);
}
export default App;
懒初始化
当初始化的值需要通过计算才能得到的值,可以将初始化函数传递给 useState 进行初始化,初始化函数只会在初次渲染时执行一次。
function lazyValue() {
console.log("lazyValue");
return 1;
}
function App() {
console.log("App render");
const [count, setCount] = useState(lazyValue);
return (
<div className="App">
<button onClick={() => setCount(count + 1)}>
count is {count}
</button>
</div>
);
}
export default App;
函数式更新
set方法支持两种调用传参方式:
- 直接更新:没有额外要求
- 函数式更新:需要依赖前一个状态时使用
// 直接更新
setCount(count + 1)
// 函数式更新
setCount(count => count + 1)
函数式更新的使用场景
function App() {
const [count, setCount] = useState(0);
return (
<div className="App">
<button onClick={() => {
// 连续执行3次加操作,按正常逻辑应该是4
setCount(count + 1)
setCount(count + 1)
setCount(count + 1)
setCount(count + 1)
}}>
count is {count}
</button>
</div>
);
}
// count: 1
上面示例点击一次按钮操作对count连加4次,结果应该是4,实际结果为1。使用函数式更新效果如下:
function App() {
const [count, setCount] = useState(0);
return (
<div className="App">
<button onClick={() => {
// 连续执行3次加操作,按正常逻辑应该是4
setCount(count => count + 1)
setCount(count => count + 1)
setCount(count => count + 1)
setCount(count => count + 1)
}}>
count is {count}
</button>
</div>
);
}
// count: 4
useRef
useRef 可以用来 存储一个可变的值 并返回一个 可变的ref对象,该值在组件的整个生命周期内持续存在。
使用场景
- 访问dom节点:可以使用 ref****对象 获取dom节点的引用
- 存储可变值:可以使用 ref 渲染保存一个可变值,该值不会引起组件的重新渲染
语法
// 初始化一个ref
const inputRef = useRef(null)
基本使用
import React, { useRef } from 'react';
function App() {
// 定义可变值
const refValue = useRef(0);
// 引用dom
const inputRef = useRef<HTMLInputElement | null>(null);
const onButtonClick = () => {
// 打印refvalue
console.log(refValue.current);
// 输入框聚焦
if (inputRef.current) {
inputRef.current.focus();
}
}
return (
<div className="App">
<input ref={inputRef} />
<button onClick={onButtonClick}>输入框聚焦</button>
</div>
);
}
export default App;
useEffect
useEffect 是一个 React Hook,它允许将组件与外部系统同步。useEffect 的执行时机在 render 渲染结束之后。
使用场景
- 为函数组件添加结束渲染信号
- 为函数组件提供组件销毁前操作
- 获取数据
- 数据订阅
- 处理dom
语法
useEffect( () => {
// 组件挂载后todo
return () => {
// 组件销毁前toto
}}, [依赖项])
基本使用
// 不传依赖项,每次渲染都会调用useEffect
useEffect(() => {
// 组件挂载后todo
})
// 依赖项传[], 首次渲染会调用
useEffect(() => {
// 组件挂载后todo
}, [])
// 依赖项传值,首次渲染和count发生改变都会调用
useEffect(() => {
// 组件挂载后todo
}, [count])
// 返回函数,
useEffect(() => {
// 组件挂载后todo
console.log('组件挂载');
return () => {
console.log('组件销毁');
}
}, [])
多值监听
当count发生改变时会同时触发两个副作用回调
useEffect(() => {
console.log("props.name 或 count 改变");
}, [props.name, count]);
useEffect(() => {
console.log("count 改变");
}, [count]);
// props.name 或 count 改变
// count 改变
清除副作用
副作用执行顺序:
-
首次渲染时不会执行return返回的函数
-
当依赖项发生改变时会先执行return返回的函数,然后执行return返回函数外的 effect 回调内容
useEffect(() => {
console.log("props.name 或 count 改变");
return () => {
console.log("清除props.name 或 count副作用");
}
}, [props.name, count]);
useEffect(() => {
console.log("count 改变");
return () => {
console.log("清除count副作用");
}
}, [count]);
// 清除props.name 或 count副作用
// 清除count副作用
// props.name 或 count 改变
// count 改变
自定义Effect
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
type IProps = {
name: string;
};
/**
* useEffect跳过首次渲染
* @param effect 回调
* @param dependencies 依赖项,依赖项改变时才执行回调
*/
function useUpdateEffect(effect: () => void, dependencies: any[]) {
// 记录回调函数是否是第一次渲染
const isFirstRender = useRef(true);
useEffect(() => {
if (isFirstRender.current) { // 第一次渲染不执行
isFirstRender.current = false;
} else { // 第二次及以后执行回调
effect();
}
}, dependencies)
}
const HelloFunction: React.FC<IProps> = (props) => {
const [count, setCount] = useState(0);
useLayoutEffect(() => {
console.log("count dom 改变");
return () => {
console.log("清除count dom副作用");
}
}, [count])
return (
<>
<button onClick={() => setCount(count + 1)}>count</button>
<div>value: {count}</div>
</>
);
}
export default HelloFunction;
useLayoutEffect
useLayoutEffect 称为有DOM操作的副作用 hooks,其作用是在DOM更新完成之后执行某个操作。useLayoutEffect 的执行时机在DOM更新完成后。
语法
useLayoutEffect( () => {
// dom更新完成
return () => {
// dom更新清除副作用
}
}, [依赖项])
基本使用
// 不传依赖项,每次渲染都会调用useLayoutEffect
useLayoutEffect(() => {
// 组件挂载后todo
})
// 依赖项传[], 首次渲染会调用
useLayoutEffect(() => {
// 组件挂载后todo
}, [])
// 依赖项传值,首次渲染和count对应dom发生改变都会调用
useLayoutEffect(() => {
// 组件挂载后todo
}, [count])
// 返回函数,
useLayoutEffect(() => {
// dom更新完成
return () => {
// dom更新清除副作用
}
}, [])
与useEffect对比
useLayoutEffect 和 useEffect 传参形式及使用方式基本一样,区别在于:
-
useLayoutEffect 监听dom更新后执行,useEffect 监听响应式数据发生改变时执行
-
useLayoutEffect 比 useEffect 先执行,因为dom更新后渲染才会进入结束流程。
useLayoutEffect(() => {
console.log("count dom 改变");
return () => {
console.log("清除count dom副作用");
}
}, [count]);
useEffect(() => {
console.log("count 改变");
return () => {
console.log("清除count副作用");
}
}, [count]);
// 清除count dom副作用
// count dom 改变
// 清除count副作用
// count 改变
useMemo
useMemo 是一个优化函数组件功能的函数,类似于Vue中的 computed,当依赖项执发生改变时执行函数返回一个新值并更新数据,useMemo 可以记忆值避免函数的依赖项没有改变时重新渲染。
语法
const doubleValue = useMemo(() => {
return 新值;
}, [依赖项]);
<div>{doubleValue}</div>
基本使用
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
type IProps = {
name: string;
};
const HelloFunction: React.FC<IProps> = (props) => {
const [count, setCount] = useState(0);
const doubleValue = useMemo(() => {
console.log("props.name 或 count 改变");
return 2 * count;
}, [count]);
return (
<>
<button onClick={() => setCount(count + 1)}>count</button>
<div>value: {count}</div>
<div>doubleValue: {doubleValue}</div>
</>
);
}
export default HelloFunction;
useCallback
说明:
1. 当依赖项为空时,useCallback修饰的函数每次渲染时都会执行,不适合复杂的技术场景
2. 适用于父组件向子组件传递函数
useCallback 是一个允许你在多次渲染中缓存函数的 React Hook,同样是一个优化性能函数。
语法
useCallback(Fn,deps)
基本使用
const getDoubleValue = useCallback(() => {
console.log("getDoubleValue");
return 2 * count;
}, []);
使用场景
父组件状态发生改变时
import React, { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
type IProps = {
name: string;
};
const FunctionChildComponent = (props: {num: number, func: () => void}) => {
useEffect(() => {
console.log("props.num 改变");
props.func && props.func();
}, [props.num])
return (
<div>
child component
</div>
)
}
const HelloFunction: React.FC<IProps> = (props) => {
const [count, setCount] = useState(1);
const getDoubleValue = useCallback(() => {
console.log("getDoubleValue");
return 2 * count;
}, [count]);
const getDoubleFunc = useCallback(() => {
console.log("getDoubleFunc");
}, []);
return (
<>
<FunctionChildComponent num={getDoubleValue()} func={getDoubleFunc} />
<button onClick={() => setCount(count + 1)}>count</button>
<div>value: {count}</div>
</>
);
}
export default HelloFunction;
useContext
useContext可以让子组件之间共享父组件传入的状态,类似vue中的 provider/inject
语法
// 父组件创建
const Context = createContext(null)
<Context.Provider value={num}>
<Item3></Item3>
<Item4></Item4>
</Context.Provider>
// 子组件使用
const num = useContext(Context)
基本使用
import React, { createContext, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
type IProps = {
name: string;
};
const Context = createContext({ count: 0 });
const FunctionChildComponent = () => {
const context = useContext(Context);
return (
<div>
child component {context.count}
</div>
)
}
const HelloFunction: React.FC<IProps> = (props) => {
const [count, setCount] = useState(1);
return (
<>
<Context.Provider value={{ count }}>
<FunctionChildComponent />
<FunctionChildComponent />
<FunctionChildComponent />
</Context.Provider>
<button onClick={() => setCount(count + 1)}>count</button>
<div>value: {count}</div>
</>
);
}
export default HelloFunction;
useReducer
useReducer 为组件提供统一状态管理的 React Hook
语法
const [state, dispacth] = useReducer(reducer,store)
基本使用
import React, { createContext, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
type IProps = {
name: string;
};
type IState = {
count: number;
};
type IAction = {
type: string;
num: number;
};
const store: IState = {
count: 1
}
const reducer = (state: IState, action: IAction) => {
switch (action.type) {
case 'increment':
return { count: action.num };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
const HelloFunction: React.FC<IProps> = (props) => {
const [state, dispatch] = useReducer(reducer, store);
const add = () => {
dispatch({ type: 'increment', num: state.count+1 });
}
return (
<>
<button onClick={add}>count</button>
<div>value: {state.count}</div>
</>
);
}
export default HelloFunction;
友情提示
见原文:【React】函数组件常见hooks)
本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。