1. useState
使用状态,用于为函数组件引入状态(state)。
const [n, setN] = React.useState(0)
数组第一项为读接口,第二项为写接口,'0'为初始值。
注意事项
1.不能局部更新
如果 state 是一个对象,我们无法部分 setState,因为 setState 不会自动合并属性。我们可以使用...将之前的属性拷贝一遍,再将需要更新的属性覆盖掉。
2.地址需要改变
如果 setState(obj) 的 obj 地址没有改变,那么 React 就会认为数据没有变化。
3.接受函数、
- useState
const [state, setState] = useState(()=>{
return initialState
})
该函数返回初始state,且只执行一次。好处是减少多余的计算过程。
- setState
setN(i => i +1)
接受一个数字,并将它加一。
2. useReducer
useState 的复杂版。
const [state, dispatch] = useReducer(reducer, initialState);
它接受一个形式如同(state, action) => newState这样的 reducer 函数,并返回一个数组。第一个元素为当前的 state ,第二个元素为其配套的 dispatch 方法。
下面是一个简单加乘操作的例子:
const initial = {
n: 0
};
const reducer = (state, action) => {
if (action.type === "add") {
return { n: state.n + action.number };
} else if (action.type === "multi") {
return { n: state.n * 2 };
} else {
throw new Error("unknown type");
}
};
function App() {
const [state, dispatch] = useReducer(reducer, initial);
const { n } = state;
const onClick = () => {
dispatch({ type: "add", number: 1 });
};
const onClick2 = () => {
dispatch({ type: "multi", number: 2 });
};
return (
<div className="App">
<h1>n: {n}</h1>
<button onClick={onClick}>+1</button>
<button onClick={onClick2}>x2</button>
</div>
);
}
3. useContext
组件之间的共享。
使用方法
- 使用
C = createContext(initial)创建上下文 - 使用
<C.provider>圈定作用域 - 在作用域内使用
useContext(C)来使用上下文 上下文是局部的全局变量。
注意事项
uesContext不是响应式的,而是重新渲染的过程。它在改变一个数的时候,是通过自顶向下逐级更新做到的。
4. useEffect
在函数组件中执行副作用操作。
对环境的改变即为副作用,如修改document.title。
useEffect 每次 render 后运行。
用途
- 作为 componentDidMount 使用,
[]作第二个参数。 - 作为 componentDidUpdate 使用,可指定依赖。
- 作为 componentWillUnmount 使用,通过 return 。
以上三种用途可同时存在;如果同时存在多个
useEffect,会按照出现次序执行。
5. useLayoutEffect
布局副作用。
它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
特点
useLayoutEffect 总是比 useEffect 先执行。
为了用户体验,优先使用 useEffect(优先渲染)。
6. useMemo useCallback
const memoizedValue = useMemo(() => value, [m, n]);
第一个参数是()=>value,第二个参数是依赖[m, n],只有当依赖变化时,才会计算出新的 value ,如果依赖不变,那么就重用之前的 value 。这种优化有助于避免在每次渲染时都进行高开销的计算。
如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。
注意
如果你的 value 是个函数,那么你就要写成useMemo(() => (x) => console.log(x)) ,这是一个返回函数的函数,我们可以使用useCallback来简化它。
useCallback
用法
useCallback(x => log(x), [m])等价于useMemo(() => x => log(x), [m])
7. useRef
useRef 返回一个可变的ref对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内持续存在。
const count = useRef(0) // 初始化
useEffect(() => {
console.log(count.current); // 读取
};
forwardRef
用于将 ref 转发给子组件。
useImperativeHandle
使用 ref 时自定义暴露给父组件的实例值。
8. 自定义 Hook
自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。
const useList = () => {
const [list, setList] = useState(null);
useEffect(() => {
ajax("/list").then(list => {
setList(list);
});
}, []); // [] 确保只在第一次运行
return {
list: list,
addItem: name => {
setList([...list, { id: Math.random(), name: name }]);
},
deleteIndex: index => {
setList(list.slice(0, index).concat(list.slice(index + 1)));
}
};
};
export default useList;
这段代码中,useList()就是一个自定义 Hook 。
使用自定义 Hook
function App() {
const { list, deleteIndex } = useList();
return (
<div className="App">
<h1>List</h1>
{list ? (
<ol>
{list.map((item, index) => (
<li key={item.id}>
{item.name}
<button onClick={() => { deleteIndex(index); }}>x</button>
</li>
))}
</ol>
) : ( "加载中..." )}
</div>
);
}