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 函数的标识是稳定的,并且不会在组件重新渲染时改变。这就是为什么可以安全地从 useEffectuseCallback 的依赖列表中省略 dispatch

useContext

深层值传递, 上下文

1
const value = useContext(MyContext);

接收一个context 对象(React.createContext的返回值),并返回该context的当前值。当前的context值由上层组件中距离当前组件最近的 <MyContext.Provider>value prop决定。

当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。即使祖先使用 React.memoshouldComponentUpdate,也会在组件本身使用 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

自定义Hooks