简介
React Hook 是 React 16.8的新特性,让函数组件能彻底取代class组件,可以实现去除state、生命周期、this绑定等操作。
React Hook详解之useState与useEffect
useState
useState主要用于状态管理,简化原先的状态管理值设置以及监听操作,以及避开了this单独绑定的操作。
代码:
import React, {useState} from 'react';
function App() {
const [count, setCount] = useState(0);
return (
<div className="App">
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
export default App;
效果:
image.png
- 首先,我们需要引入useState这个方法;
- useState在使用的时候传入一个初始值,这个函数会返回一个数组,数组中有两个变量;
- 数组的第一个值是一个变量,变量值就是传入的参数;
- 数组的第二个值是一个方法,用于对初始值进行修改;
- 后面我们可以额直接使用这个变量和方法,不涉及到this,就只相当于函数内部的变量,在return的时候使用到这些变量。
多个useState
代码:
import React, {useState} from 'react';
function App() {
const [count, setCount] = useState(0);
const [age, setAge] = useState(10);
return (
<div className="App">
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
<p>Your age if {age}</p>
<button onClick={() => setAge(age + 1)}>Add age</button>
</div>
)
}
export default App;
效果:
image.png
useEffect(只传一个回调函数)
我们通过useState可以实现在函数组件中取代原乡class组件的state,那么原先组件的生命周期该怎么办呢?这里,我们需要使用useEffect
代码:
import React, {useState, useEffect} from 'react';
function Counter() {
const [count, setCount] = useState(0)
const [age, setAge] = useState(20)
// 相当于 componentDidMount 和 componentDidUpdate
useEffect(() => {
console.log(`You clicked ${count} times`)
})// 监控所有的state
return (
<div className="App">
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Add Count</button>
<p>You are {age} years old!</p>
<button onClick={() => setAge(age + 1)}>Add Age</button>
</div>
)
}
export default Counter;
效果:
image.png
- 我们先引入useEffect;
- useEffect相当于 componentDidMount 和 componentDidUpdate,也就是初始化以及更新的时候就会触发传进去的函数
useEffect(传入第二个参数)
useEffect返回值的箭头函数用于执行清除,第二个参数为不空的数组表示只监控该state的状态。
代码:
import React, {useState, useEffect} from 'react';
function Counter() {
const [count, setCount] = useState(0)
const [age, setAge] = useState(20)
useEffect(() => {
console.log('挂载和状态更新时执行')
return () => {
console.log('状态更新和卸载组件的时候执行')
}
}, [count]) // 只监控count状态
return (
<div className="App">
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Add Count</button>
<p>You are {age} years old!</p>
<button onClick={() => setAge(age + 1)}>Add Age</button>
</div>
)
}
export default Counter;
效果:
image.png
- useEffect的第一个参数为一个箭头函数,这个箭头函数会在挂载和状态更新的时候执行;箭头函数可以再返回一个箭头函数,这个返回的箭头函数会在状态更新和卸载组件的时候更新;
- useEffect可以加第二个参数,为不空的数组,表示需要监控的状态;不是这个数组里面的不进行监控;
useEffect(第二个参数为空数组)
useEffect第二个参数为空数组,等价于componentDidMount
代码:
import React, {useState, useEffect} from 'react';
function Counter() {
const [count, setCount] = useState(0)
const [age, setAge] = useState(20)
// 箭头函数返回箭头函数的简单写法
useEffect(() => () => {
console.log('卸载执行')
}, [])
useEffect(() => {
console.log('首次时执行')
return () => {
console.log('仅卸载时执行')
}
}, []) // 都不监控,只在第一次挂载的时候执行一次
return (
<div className="App">
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Add Count</button>
<p>You are {age} years old!</p>
<button onClick={() => setAge(age + 1)}>Add Age</button>
</div>
)
}
export default Counter;
效果:
image.png
使用React Hook的规则
- 只在最顶层使用 Hook
- 不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。
- 只在 React 函数中调用 Hook
- 可以在 React 的函数组件中调用 Hook
- 可以在自定义 Hook 中调用其他 Hook
手动实现一个简单的React Hook
代码:
import React, {useState, useEffect} from 'react';
import ReactDOM from 'react-dom'
let memoizedState = []; // hooks 存放在这个数组
let cursor = 0; // 当前 memoizedState 下标
function render() {
cursor = 0;
ReactDOM.render(
<App />,
document.getElementById('root')
)
}
function useState(initialValue) {
memoizedState[cursor] = memoizedState[cursor] || initialValue;
const currentCursor = cursor;
function setState(newState) {
memoizedState[currentCursor] = newState;
render()
}
return [memoizedState[cursor++], setState]; // 返回当前state,并把cursor加1
}
function useEffect(callback, depArray) {
const hasNoDeps = !depArray;
const deeps = memoizedState[cursor];
const hasChangeDeps = deps
? !depArray.every((el, i) => el === deps[i])
: true;
if (hasNoDeps || hasChangedDeps) {
callback();
memoizedState[cuesor] = depArray;
}
cursor++;
}
function App() {
console.log('render app')
const [count, setCount] = useState(100);
const [name, setName] = useState('hunger')
useEffect(() => {
console.log('update', count)
}, [count])
return (
<div className="App">
<p>You clicked {count} times</p>
<button onClick={() => setCpunt(count + 1)}>Add count</button>
<p>Your name is {name}</p>
<button onClick={() => setName(name + '!')}>Set name</button>
</div>
)
}
export default App;