hooks 钩子函数都是以 use 开头命名的,当我们使用自定义hooks时也需要遵循这一点,hooks必须写在组件内部,否则会报错。
useState
useState 接收一个初始值,可以时任意类型的值,返回一个数组,数组的第一项为整个 state 的值,第二项为改变整个 state 的方法。
import React, { useState } from 'react';
const [text, setText] = useState('random title');
const handleClick = () => {
setText('hello world');
};
return (
// 当我们使用 React.Fragment 时,可以简写为一对尖括号
<>
<h1>{text}</h1>
<button type='button' className='btn' onClick={handleClick}>
change title
</button>
</>
);
};
异步处理
当我们在设置 state 的值的时候,如果是采用异步的方式设置的,那么当异步的结果没回来之前,就进行下一次设置,state 的值还是只会在初始值上修改的。为了保证异步的时候设置的值依赖上次修改后的值,我们可以使用 setState 的另一种用法。
const asyncIncrease = () => {
setTimeout(() => {
// setValue(value + 1); 这样设置时获取的value值不是最新的值
setValue((prevState) => {
// 必须有返回值
return prevState + 1;
});
}, 2000);
};
useEffect
useEffect 会在每次组件渲染后重新执行,当我们触发 useState 的 setState 修改数据的时候会使组件重新渲染。useEffect 会被用于处理一些副作用,比如发送请求、事件监听、注册订阅等。
useEffect 接收2个参数:
- 回调函数,重新渲染时执行的方法。
- 依赖数组,当数组中的项改变时才会重新执行回调函数。当依赖数组为一个空数组时,意味着只会在初始渲染时运行一次。
如果你在 useEffect 中修改了 state ,那么请一定记得添加依赖数组,否则会进行无限循环,每次修改了 state 触发重新渲染,重新渲染会执行 useEffect......
清除函数
初次渲染组件时,我们会直接执行useEffect 中的函数,而不执行 useEffect 的清理函数。而且在组件销毁的时候会执行 useEffect 的清理函数。但是在重新渲染时,会先执行 useEffect 的清理函数,再执行 useEffect 中的函数。useEffect 的清理功能就是在 useEffect 中返回一个函数。
useEffect(() => {
window.addEventListener('resize', checkSize);
return () => {
window.removeEventListener('resize', checkSize);
};
});
异步处理
useEffet 中的函数不能使用 async/await,因为 async 返回的是一个 promise,而 useEffet 返回的应该是它的清理函数。但是我们可以单独将异步函数写在外面,然后在useEffet 中调用异步函数。
受控组件 input
受控组件经常绑定 state 值,而不受控组件经常使用 useRef。
当我们向 input 上绑定了 value 之后,就一定要绑定 onChange 事件。否则会报错且输入将不产生任何变化。
const ControlledInputs = () => {
const [email, setEmail] = useState('');
return (
<>
<input type='text' id='email' name='email'
value={email}
onChange={(e) => setEmail(e.target.value)} />
</>
);
};
useRef
useRef 和 useState 一样也可以保存数据,但是改变它不会触发重新渲染,最常用的就是使用它来指定 dom 元素。使用 useRef 创建的是一个带有 current 属性的对象,通过 ref 可以将 current 绑定在这个元素上。
const UseRefBasics = () => {
const refContainer = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
console.log('handleSubmit', refContainer);
};
useEffect(() => {
console.log('useEffect', refContainer);
});
console.log('render', refContainer);
return (
<>
<form className='form' onSubmit={handleSubmit}>
<div>
<input type='text' ref={refContainer} />
</div>
<button type='submit'>submit</button>
</form>
</>
);
};
运行上面的例子,当我们在 render 的时候取到的 refContainer 中 current 的值还是 null,但是在执行 useEffet 的时候,我们可以看到已经绑定到 input 元素上了。
我们可以使用 refContainer.current.focus() 进行 input 元素聚焦,也可以通过 refContainer.current.value 获取当前输入框的值。
useReducer
简单情况用 useState,复杂情况可以使用 useReducer。使用 useReducer 可以将多个 state 数据的处理放在一个地方,只需要 dispatch 传递不同参数进行不同处理,使逻辑更加清晰。
参数: 1. reducer: 改变数据时触发的方法。 2. defaultState: 数据默认初始值。
返回值: 1. state: 存储数据的对象。 2. dispatch: 改变数据的方法。
const defaultState = {people: [], isModalOpen: false, modalContent: ''};
const [state, dispatch] = useReducer(reducer, defaultState);
reducer
接收2个参数,第一个参数为当前的数据,第二个是 dispatch 传递参数,可以根据这个类型进行不同的处理。reducer 方法必须要有返回,否则报错。
export const reducer = (state, action) => {
if (action.type === 'ADD_ITEM') {
const newPeople = [...state.people, action.payload];
return { ...state, people: newPeople, isModalOpen: true, modalContent: 'item added'};
}
if (action.type === 'NO_VALUE') {
return { ...state, isModalOpen: true, modalContent: 'please enter value' };
}
if (action.type === 'CLOSE_MODAL') {
return { ...state, isModalOpen: false };
}
// ... 更多情况处理
return { ...state, people: newPeople };
}
};
dispatch
触发数据的修改,可以传递不同参数。
dispatch({ type: 'NO_VALUE' });
StrictMode
React.StrictMode 会引起组件中的代码被执行两次。