React hooks
React Hooks
参考 https://juejin.cn/post/6906401150929469453?utm_source=gold_browser_extension
useState
1
2
3
4
5
6
|
import { useState } from "react"
const FuncComp = (props) => {
const [count, setCount] = useState(0)
return (<h3>Count: {count}<button onClick={() => setCount(count+1)}></button></h3>)
}
export default FuncComp
|
useEffect
副作用,正常的一个组件应该只返回页面。
-
组件每次执行render之后 useEffect 都会调用,此时相当于执行类组件的componentDidMount 和 componentDidUpdate生命周期。
-
传入一个空数组[], 此时useEffect只会调用一次,相当于执行类组件的componentDidMount 和 componentWillUnmount生命周期。
-
传入一个数组,其中包括变量,只有这些变量变动时,useEffect 才会执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
import React, { useState, useEffect } from "react"
const FuncComp = (props) => {
const [count, setCount] = useState(0)
// [count] 能够引起副作用的依赖,只有count发生变化时才会执行
// 一个useEffect负责一件事
useEffect(() => {
console.log('useEffect', count)
if (count % 2 === 0) {
console.log('count', count);
}
}, [count])
// 组件挂载 卸载
useEffect(() => {
// 组件挂载时执行 componentDidMount
console.log('挂载')
const handleClick = () => console.log('handleClick')
document.addEventListener('click', handleClick)
// 如果[] 为空相当于组件被卸载时执行的操作。 componentDidUnmount()
return () => {
// 组件卸载时执行的操作, componentWillUnmount
console.log('卸载')
document.removeEventListener('click', handleClick)
}
}, [])
useEffect(() => {
//相当于componentDidUpdate
document.title = count;
})
return (<h3>Count: {count}<button onClick={() => setCount(count+1)}></button></h3>)
}
export default FuncComp
|
useReducer
1
|
const [state, dispatch] = useReducer(reducer, initialArg, init);
|
useState](https://zh-hans.reactjs.org/docs/hooks-reference.html#usestate) 的替代方案。它接收一个形如
(state, action) => newState的 reducer,并返回当前的 state 以及与其配套的
dispatch` 方法。
在某些场景下,useReducer
会比 useState
更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer
还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch
而不是回调函数 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
import React, { useReducer } from "react"
const FuncComp = (props) => {
const initialState = {count: 0, name: 'kyle'}
const reducer = (state, action) => {
// payload 默认1
const {type, payload = 1} = action
switch (type) {
case 'increment':
// return 返回新对象, 要返回所有的值
return {...state, count: state.count + payload};
case 'decrement':
return {...state, count: state.count - 1};
default:
throw new Error();
}
}
const [state, dispatch] = useReducer(reducer, initialState)
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement', payload: 2})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
export default FuncComp
|
React 会确保 dispatch
函数的标识是稳定的,并且不会在组件重新渲染时改变。这就是为什么可以安全地从 useEffect
或 useCallback
的依赖列表中省略 dispatch
。
深层值传递, 上下文
1
|
const value = useContext(MyContext);
|
接收一个context 对象(React.createContext
的返回值),并返回该context的当前值。当前的context值由上层组件中距离当前组件最近的 <MyContext.Provider>
的 value
prop决定。
当组件上层最近的 <MyContext.Provider>
更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext
provider 的 context value
值。即使祖先使用 React.memo
或 shouldComponentUpdate
,也会在组件本身使用 useContext
时重新渲染。
调用了 useContext
的组件总会在 context 值变化时重新渲染。如果重渲染组件的开销较大,你可以 通过使用 memoization 来优化。
useContext(MyContext)
只是让你能够读取 context 的值以及订阅 context 的变化。你仍然需要在上层组件树中使用 <MyContext.Provider>
来为下层组件提供 context。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
const ThemeContext = React.createContext('#000000');
function App() {
const [bgColor, setBgColor] = useState('#000000')
return (
// value 要传值
<input type="color" onChange={ev => setBgColor(ev.target.value)}></input>
// 生产
<ThemeContext.Provider value={bgColor}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
// 消费
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme, color: theme }}>
I am styled by theme context!
</button>
);
}
// 消费的第二种方式
function ThemedButton2() {
return (
<ThemeContext.Consumer>
{ value => {
<button style={{ background: value, color: value }}>
I am styled by theme context!
</button>
}}
</ThemeContext.Consumer>
);
}
|
useRef
1
|
const refContainer = useRef(initialValue);
|
useRef
返回一个可变的 ref 对象,其 .current
属性被初始化为传入的参数(initialValue
)。返回的 ref 对象在组件的整个生命周期内保持不变。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import React, { useRef} from 'react';
// 刷新页面自动聚焦 inputDom.focus(),
function Input() {
const inputRef = useRef()
useEffect(() => {
console.log('mount')
inputRef.current.focus()
}, [])
return <input ref={inputRef}/>
}
export default function RefDemo() {
return (
<div>
<Input />
</div>
)
}
|
在RefDemo内聚焦.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import React, { useRef } from 'react';
// 刷新页面自动聚焦 inputDom.focus(), 在RefDemo内聚焦.
const Input = React.forwardRef((props, ref) => {
// {...props}会涉及污染
return <input {...props} ref={inputRef}/>
})
export default function RefDemo() {
const inputRef = useRef()
useEffect(() => {
console.log('mount')
inputRef.current.focus()
inputRef.current.value = "hell word"
}, [])
return (
<div>
<Input placeholder="ref demo" ref={inputRef}/>
</div>
)
}
|
useCallback & useMemo