useState
useState 可以让你的函数组件也具备类组件的 state 功能
使用语法如下:
const [state, setState] = useState(initialState);
useState 返回一个数组,第一个是 state 的值,第二个是更新 state 的函数
在真实的程序中我们可以这样使用:
function TestUseState() {
const [count, setCount] = React.useState(0);
return (
<div>
<p>useState api</p>
<p>Count: {count} <button onClick={() => setCount(count + 1) }>自增</button></p>
</div>
)
}
使用 useState 需要注意一个事项,当你初始化是一个对象时,使用 setCount 不像类组件的 this.setState 会自动合并到 state 中。setCount 会使用当前的值覆盖之前的 state。如下所示:
function TestUseStateObject() {
const [state, setState] = React.useState({
count: 0,
greeting: "Hello, World!",
});
const handleAdd = () => {
setState({
count: state.count + 1
})
}
console.log('state > ', state)
return (
<div>
<p>useStateObject api</p>
<p>Count: {state.count} <button onClick={handleAdd}>自增</button></p>
</div>
)
}
我们可以看到,当点击按钮时 state 被替换成了 {count: 1}。如果想要在 state 中使用一个对象需要在更新值的时候把之前的值解构出来,如下所示:
setState({
...state,
count: state.count + 1
})
useEffect
默认情况下 useEffect 在完成渲染后运行,我们可以在这里获取 DOM 和处理其他副作用。但它还有两种不同的运行阶段
function TestUseEffect() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
console.log(`组件被更新,Count: ${count}`);
});
return (
<div>
<p>useEffect api</p>
<p>Count: {count} <button onClick={() => setCount(count + 1) }>自增</button></p>
</div>
)
}
上面的 useEffect 在每次组件渲染后运行,每当我们点击自增按钮都会执行一次
但是如果上面的代码在每次渲染后都执行,如果我们在 useEffect 从服务器拉取数,造成的结果就是每次渲染后都会从服务器拉取数,或者是只有某些 props 被更新后才执行 useEffect。那么默认的 useEffect 就不是我们想要的执行方式,这时 useEffect 提供了第二个参数
useEffect(disUpdate, [])
useEffect 第二个参数为一个数组。当我们提供第二个参数时,只有第二个参数被更改 useEffect 才会执行。利用第二个参数我们可以模拟出类组件的 componentDidMount 生命周期函数
function TestUseEffectListener() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
console.log('componentDidMount fetch Data...');
}, []);
return (
<div>
<p>TestUseEffectListener</p>
<p>Count: {count} <button onClick={() => setCount(count + 1) }>自增</button></p>
</div>
)
}
上面的代码中 useEffect 只会执行一次,当你点击自增 useEffect 也不会再次执行
在 useEffect 第一个参数的函数中我们可以返回一个函数用于执行清理功能,它会在 UI 组件被清理之前执行,结合上面所学的知识使用 useEffect 模拟 componentWillUnMount 生命周期函数
function TestUseEffectUnMount() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
return () => {
console.log('componentUnmount cleanup...');
}
}, []);
return (
<div>
<p>TestUseEffectUnMount</p>
</div>
)
}
上面的代码中,当组件 TestUseEffectUnMount 将要销毁时,会执行 console.log() 代码
useContext
useContext 可以让你在函数中使用 context,它有效的解决了以前 Provider、Consumer 需要额外包装组件的问题
使用语法如下:
const context = useContext(Context);
现在让我们来看看实际应用中这个 useContext 是如何使用的,代码如下:
function TestFuncContext() {
const context = React.useContext(ThemeContext);
return (
<div style={context}>TestFuncContext</div>
)
}
我们可以看到上面直接使用 React.useContext(ThemeContext) 就可以获得 context,而在之前的版本中需要像这样才能获取
// 之前Consumer的访问方式
function TestNativeContext() {
return (
<ThemeContext.Consumer>
{(value) => {
return (
<div style={value}>TestNativeContext</div>
)
}}
</ThemeContext.Consumer>
);
}
useReducer
useReducer 是 useState 的替代方案,当你有一些更复杂的数据时可以使用它
使用语法如下:
const [state, dispatch] = useReducer(reducer, initialState);
第一个参数是一个 reducer 用来处理传过来的 action,函数申明为:(state, action) => ();第二个参数是一个初始化的 state 常量
在返回值 [state, dispatch] 中,state 就是你的数据,dispatch 可以发起一个 action 到 reducer 中处理。这个功能感觉就是组件本地的redux,在设计一些复杂的数据结构时可以使用
现在我们看看实际应用中这个 useReducer 是如何使用的,代码如下:
function TestUseReducer() {
const [state, setState] = React.useReducer((state, action) => {
switch(action.type) {
case 'update':
return {name: action.payload}
default:
return state;
}
}, {name: ''});
const handleNameChange = (e) => {
setState({type: 'update', payload: e.target.value})
}
return (
<div>
<p>你好:{state.name}</p>
<input onChange={handleNameChange} />
</div>
)
}
当改变 input 中的值时会同时更新 state 中的数据,然后显示在界面上
useCallback
useCallback 和 useMemo 有些相似,它接收一个内联函数和一个数组,它返回的是一个记忆化版本的函数
使用语法如下:
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
useCallback 的第一个参数是一个函数,用来执行一些操作和计算;第二个参数是一个数组,当这个数组里面的值改变时 useCallback 会重新执行更新这个匿名函数里面引用到的值。这样描述可能有点不太好理解,下面看一个例子:
function TestUseCallback({ num }) {
const memoizedCallback = React.useCallback(
() => {
// 一些计算
return num;
},
[],
);
console.log('记忆 num > ', memoizedCallback())
console.log('原始 num > ', num);
return (
<div>
<p>TestUseCallback</p>
</div>
)
}
如果我们想监听 num 值的更新重新做一些操作和计算,我们可以给第二个参数放入 num 值,像下面这样:
function TestUseCallback({ num }) {
const memoizedCallback = React.useCallback(
() => {
// 一些计算
return num;
},
[num],
);
console.log('记忆 num > ', memoizedCallback())
console.log('原始 num > ', num);
return (
<div>
<p>TestUseCallback</p>
</div>
)
}
useRef
useRef 的功能有点像类属性,或者说你想要在组件中记录一些值,并且这些值在稍后可以更改
使用语法如下:
const refContainer = useRef(initialValue);
useRef 返回一个可变的对象,对象的 current 属性被初始化为传递的参数(initialValue),返回的对象将持续整个组件的生命周期
一个保存 input 元素,并使其获取焦点程序,代码如下:
function TestUseRef() {
const inputEl = React.useRef(null);
const onButtonClick = () => {
// 点击按钮会设置input获取焦点
inputEl.current.focus(); // 设置useRef返回对象的值
};
return (
<div>
<p>TestUseRef</p>
<div>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>input聚焦</button>
</div>
</div>
)
}
useRef 返回的对象可以在其他地方设置比如:useEffect、useCallback 等