hooks&ahooks

简介

在不使用class的情况下使用state以及其他react特性
=> 不论是有状态组件还是无状态组件都可以用函数来声明

useState

useState通过在函数组件里调用它来给组件添加一些内部 state
useState会返回一对值:当前状态一个让你更新它的函数
你可以在事件处理函数中或其他一些地方调用这个函数。
(类似 this.setState,但是它不会把新的 state 和旧的 state 进行合并)
useState的参数:state的初始值

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
// 引入 useState
import React,{useState} from 'react';

export default function Test(){
// 声明state变量count和更新count的方法setCount (解构赋值)
const [count,setCount] = useState(18) // 赋给count的初始值为18
return(
<div>
<h3>
count: {count} {/* count的使用 */}
</h3>
{/* setCount的使用 */}
<button onClick={()=>{setCount(count+1)}}>add</button>
</div>
)
}

// Class 方法
class Test extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
render() {
return (
<div>
<h3>
count: {this.state.count}
</h3>
<button onClick={()=>this.setState({ count: this.state.count + 1 })}>add</button>
</div>
);
}
}

声明多个state变量

1
2
3
4
5
6
7
function Test() {
// state的值是按顺序存储调用的所以不能对他的声明进行条件判断
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}

有个问题,我按照官方文档给的示例代码写的时候
useState(..)会标红
报错:

React Hook “useState” is called in function “test” which is neither a React function component or a custom React Hook function

后面发现是函数命名的问题
解决方法:

  1. 函数名/组件名 首字母大写
  2. 驼峰命名法在前面+use也可 (useTest)

useEffect

effect hook在函数组件中执行副作用操作 (异步的)
副作用: 用来代替生周期、是componentDidMount,componentDidUpdate,componentWillUnmount的组合(替代品)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React,{useState,useEffect} from 'react';

export default function Test(){
const [count,setCount] = useState(18)
const [age,setAge] = useState(0)
useEffect(()=>{
document.title = count
},[count])
// 可接收第二个参数 只有在 count`改变时`触发更新 类似于computed
return(
<div>
<h3>
count: {count}
</h3>
<h3>
Age: {age}
</h3>
<button onClick={()=>{setCount(count+1)}}>add</button>
<button onClick={()=>{setAge(age+10)}}>add</button>
</div>
)
}

useContext 状态管理类似于eventBus

  1. 公共状态存储文件 createContent.tsx
1
2
3
4
5
import { createContext } from "react";

const myContext = createContext(null)

export default myContext;
  1. 父组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { Button } from 'antd';
import React, { createContext, useState } from 'react';
import myContext from './createContext';
import ChildPage from './components/ChildPage'

export default () => {
const [count, setCount] = useState<number>(0)

return (
<div>
<h1>父组件</h1>
<Button onClick={() => setCount(count + 1)}>点击 {count}</Button>

<hr />

// 提供器
<myContext.Provider value={count}>
<ChildPage />
</myContext.Provider>
</div>
);
};

  1. 子组件
1
2
3
4
5
6
7
8
9
10
11
import React, { useContext } from "react"
import myContext from "../createContext"

const ChildPage: React.FC<any> = () => {

const count = useContext(myContext) // 通过useContext直接获取到context数据

return (<h1>子组件:{count}</h1>)
}

export default ChildPage

useReducer 需要逻辑处理state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 初始值
const initialState = { count: 0 }
// 数据操作
const reducer = (state: { count: number; }, action: { type: string; }) => {
switch (action.type) {
case "add": return { count: state.count + 1 };
case "del": return { count: state.count - 1 };
default: throw new Error()
}
}

// 数据声明
const [state, dispatch] = useReducer(reducer, initialState)


// 使用

<h1>Count:{state.count}</h1>
<Button onClick={() => dispatch({ type: 'add' })}>reducer add</Button>
<Button onClick={() => dispatch({ type: 'del' })}>reducer del</Button>

useEffect & useMemo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const dom = useRef()
const state = useReactive({ num: 1, childInfo: 'hello' })

useEffect(() => {
// mounted 的时候执行 模板已经挂在到页面上,dom可以获取到
console.log('render effect', dom, state.num)
// dom -> { current:HTMLElement }
}, [state.num])

useMemo(() => {
// 相当于create 的时候执行 模板没有挂在到页面上,dom获取不到
console.log('render memo', dom, state.num)
// dom -> { current:undefined }
}, [state.num])

useReactive 响应式数据,直接修改属性可以刷新视图

1
const state = useReactive(initialState: Record<string,any>) // 参数为一个对象

React.memo && useCallback()

在父组件中引用子组件,只要父组件有数据改变都会触发子组件刷新
React.memo()用来控制是否触发子组件刷新

React.memo()

接收两个参数,第一个是纯函数的组件,第二个参数用于对比props控制是否刷新
如果不传第二个参数的话则是对props进行浅对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React, { useEffect } from "react"
interface ChildProps {
count: number
}
const Child: React.FC<ChildProps> = ({count}) => {
console.log('child render')

return <>
child
count: {count}
</>
}

function isRefresh(prevProps: ChildProps, nextProps: ChildProps) {
if (prevProps.count === nextProps.count) {
return true
}
return false
}

export default React.memo(Child, isRefresh)

useCallback

缓存的是函数、优化子组件防止组件的重复渲染
返回一个函数,只有在以来变化的时候才会更新

  • 通过父组件传递给子组件的方法被调用时,会触发所有子组件更新,不管关联的值是否改变
  • 如果不用useCallback一个输入框改变会触发两个输入框组件重新渲染
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// child.tsx
interface ChildProps {
count: number
onChange: any
}
const Child: React.FC<ChildProps> = ({ count, onChange }) => {

useEffect(() => {
console.log('%c child refresh', 'color:red')
})

return <>
child
<input value={count} onChange={onChange} />
</>
}
export default React.memo(Child)


// parent.tsx
export default () => {
let [childCount, setChildCount] = useState<number>(0)
let [childCount2, setChildCount2] = useState<number>(0)

useEffect(() => {
console.log('%c parent refresh', 'color:blue')
}, [])


// const handleChange = (e) => {
// console.log('%c change', 'color:yellow')
// setChildCount(e.target.value)
// }

// const changeChild = (e) => {
// console.log('child change')
// setChildCount2(e.target.value)
// }


const handleChange = useCallback((e) => {
console.log('%c change', 'color:yellow')
setChildCount(e.target.value)
}, [])

const changeChild = useCallback((e) => {
console.log('child change')
setChildCount2(e.target.value)

}, [])

return (
<div style={{ padding: '50px' }}>
<Child count={childCount} onChange={handleChange} />
<br />
<Child count={childCount2} onChange={changeChild} />
</div>
)
}

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2019-2023 John Doe
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信