文章目录

  • ​​使用hook限制​​
  • ​​常用 hook 函数​​
  • ​​1、useState​​
  • ​​2. 受控组件​​
  • ​​3. useEffect​​
  • ​​useEffect模拟componentDidMount​​
  • ​​模拟componentDidMount/componentDidUpdate​​
  • ​​模拟componentDidMount/componentWillUnmount​​
  • ​​模拟componentDidMount/componentDidUpdate/componentWillUnmount​​
  • ​​网络请求​​
  • ​​第一种写法​​
  • ​​第二种写法:​​
  • ​​第三种写法​​
  • ​​4. useReducer​​
  • ​​使用useReducer完成todolist​​
  • ​​5. useContext​​
  • ​​6. useMemo​​
  • ​​7. useCallback​​
  • ​​8. React.memo​​
  • ​​9. useRef​​
  • ​​10. useImperativeHandle​​
  • ​​ref 的使用​​
  • ​​11. useLayoutEffect​​
  • ​​12. react-redux-hook​​
  • ​​同步​​
  • ​​使用redux完成异步​​
  • ​​13. react-router-dom-hook​​
  • ​​14. 路由懒加载和拆分​​

使用hook限制

[react] hook_前端

常用 hook 函数

1、useState

(1)、简单实例
在函数组件中,用 useState 实现计数器功能

import React, { useState } from 'react';

function Example() {
// 声明 `count` 的 state 变量 和 用于改变 `count` 的setCount方法;
const [count, setCount] = useState(0);

// count 和 setCount 是函数内部的变量和方法,可直接访问
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}

(2) 可使用多个state

function ExampleWithManyStates() {
// 声明多个 state 变量,且他们都是相互独立的
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: '学习 Hook' }]);

(3)、函数式更新
​​​setCount( c => c +1 )​​ 这种方式就是函数式更新,确保了 count 更新总是建立在最新的数据上,让你从 count 的管理中解脱出来。如下实例,一个计数器,count 数值的变化,永远基于最新数值,让你不用去管理count。

function Counter({ initialCount }) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}

备注:此功能用在 useEffect 中,会非常方便。

(4)、惰性初始化
useState 的初始化,可以通过传入函数的方式进行,适用于复杂计算后的结果。

const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});

(5)、跳过 state 更新
调用 useState 的更新方法来更新state,如果新 state 数值跟之前是一样的,那么组件会跳过子组件的渲染和 useEffect 的调用。

2. 受控组件

import React, { useState } from 'react';

const Child = () => {
let [username, setUsername] = useState('')
let [password, setPassword] = useState('')

// 登录
const dologin = () => {
console.log(username, password);
}

return (
<div>
<div>
账号:
{/* 受控组件 */}
<input type="text" value={username} onChange={e => setUsername(e.target.value.trim())} />
</div>
<div>
密码:
<input type="text" value={password} onChange={e => setPassword(e.target.value.trim())} />
</div>
<div>
<button onClick={dologin}>进入系统</button>
</div>
</div>
);
}

export default Child;

[react] hook_前端_02


[react] hook_App_03


[react] hook_javascript_04

3. useEffect

useEffect模拟componentDidMount

挂载时候执行

import React, { useState, useEffect } from 'react';
// 【useEffect】它是可以在函数组件中模拟出类组件中最常用的3个生命周期方法
// 它可以被调用N次
// componentDidMount componentDidUpdate componentWillUnmount
// 【注】: useEffect中不能使用async/await,useEffect执行行完要销毁,如果你返回了一个对象,则无法销毁

const Child = () => {
let [age, setAge] = useState(10)

// 模拟componentDidMount
// 参数1:回调函数,参数2:可选项,如果为一个空数组,则模拟componentDidMount
// useEffect(()=>{},[])
useEffect(() => {
console.log('模拟componentDidMount1');
}, [])


return (
<div>
<button onClick={() => setAge(age => age + 1)}>自增年龄 -- {age}</button>
</div>
);
}

export default Child;

[react] hook_react.js_05

模拟componentDidMount/componentDidUpdate

  1. 只写参数1:回调函数
    全局的数据更新都会触发它
  2. [react] hook_前端_06

  3. 指定state数据更新才触发,必须写参数2,依赖项
  4. [react] hook_javascript_07

写多个参数

[react] hook_react.js_08

模拟componentDidMount/componentWillUnmount

挂载时候执行,组件销毁执行

[react] hook_javascript_09

模拟componentDidMount/componentDidUpdate/componentWillUnmount

挂载时候执行,数据更新时它会执行,组件销毁执行

[react] hook_前端_10

[react] hook_数据_11

网络请求

第一种写法

[react] hook_数据_12

第二种写法:

[react] hook_App_13

第三种写法

[react] hook_数据_14

[react] hook_前端_15

[react] hook_前端_16

import { useState, useEffect } from 'react';
import { getSwiperApi } from '@/api'

const useHttpSwiper = () => {
let [data, setData] = useState([])

const loadData = async () => {
let ret = await getSwiperApi()
setData(data => [...data, ...ret.data])
}
// useEffect它不能使用async/await
useEffect(() => {
loadData()
}, [])

return [
data,
loadData
]
}

export default useHttpSwiper
import useHttpSwiper from "@/hooks/useHttpSwiper";

const Child = () => {
let [data, loadData] = useHttpSwiper()

return (
<div>
{
data.map((item, index) => (
<li key={index}>{item.title}</li>
))
}
<hr />
<button onClick={() => loadData()}>加载更多数据</button>
</div>
);
}

export default Child;

4. useReducer

useReducer 这个 Hooks 在使用上几乎跟 Redux一模一样,唯一缺少的就是无法使用 redux 提供的中间件。

[react] hook_javascript_17

import React, { useReducer } from 'react';
// useReducer可以理解为,精简版的redux,写法和redux一致
const initState = {
count: 100
}

const reducer = (state, { type, data }) => {
if ('add' === type) return { ...state, count: state.count + data }
return state
}

const Todo = () => {
// 参数1:纯函数,用来完成state数据的修改
// 参数2:初始数据
// 参数3:惰性初始数据,如果有参数3,则参数2,无效了 回调函数,要返回一个对象
let [state, dispatch] = useReducer(reducer, initState)

return (
<div>
<h3>{state.count}</h3>
<button onClick={() => dispatch({ type: 'add', data: 2 })}>++++</button>
</div>
);
}

export default Todo;

[react] hook_javascript_18


[react] hook_App_19

使用useReducer完成todolist

src/App.jsx

[react] hook_数据_20


src/Todo/reducer.js

[react] hook_javascript_21

src/Todo/index.jsx

[react] hook_javascript_22


src/Todo/ui/Todoform.jsx

[react] hook_数据_23


src/Todo/ui/Todoitems.jsx

[react] hook_javascript_24


src/hooks/userInput.js

[react] hook_数据_25

[react] hook_数据_26

[react] hook_App_27

5. useContext

App.js

[react] hook_数据_28


src/context/app.js

[react] hook_javascript_29


child.jsx

[react] hook_App_30

[react] hook_App_31

6. useMemo

记忆组件,可以理解为计算属性 性能优化

[react] hook_前端_32

7. useCallback

记忆函数,它计算返回一个缓存函数。

[react] hook_javascript_33

[react] hook_javascript_34

8. React.memo

给函数组件来减少重复渲染的顶层Api,类似于PureComponent

import React  from 'react';

export default class extends React.Component {
constructor(props){
super(props);
this.state = {
date : new Date()
}
}

componentDidMount(){
setInterval(()=>{
this.setState({
date:new Date()
})
},1000)
}

render(){
return (
<div>
<Child seconds={1}/>
<div>{this.state.date.toString()}</div>
</div>
)
}
}
function Child({seconds}){
console.log('I am rendering');
return (
<div>I am update every {seconds} seconds</div>
)
};
//memo 用来减少无用的重复渲染 默认只能对于普通类型进行比较
export default React.memo(Child)

React.memo()可接受2个参数,第一个参数为纯函数的组件,第二个参数用于对比props控制是否刷新,与​​shouldComponentUpdate()​​功能类似

import React from "react";

function Child({seconds}){
console.log('I am rendering');
return (
<div>I am update every {seconds} seconds</div>
)
};

function areEqual(prevProps, nextProps) {
if(prevProps.seconds===nextProps.seconds){
return true
}else {
return false
}

}

// 针对于引用类型 进行比较
export default React.memo(Child,areEqual)

9. useRef

useRef 跟 createRef 类似,都可以用来生成对 DOM 对象的引用

useRef默认只能在html标签中使用,不可以在函数组件中使用

[react] hook_App_35

给自定义函数组件使用ref属性

[react] hook_App_36

[react] hook_App_37

[react] hook_react.js_38

10. useImperativeHandle

ref 的使用

普通的类组件有实例所以可以用过 React.createRef() 挂载到节点或组件上,然后通过 this 获取到该节点或组件。

class RefTest extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
}
componentDidMount(){
console.log(this.myRef.current);
}
render(){
return <input ref={this.myRef}/>
}
}

正常情况下 ​​ref​​​ 是不能挂在到函数组件上的,因为函数组件没有实例,但是 ​​useImperativeHandle​​ 为我们提供了一个类似实例的东西。它帮助我们通过useImperativeHandle 的第 2 个参数,所返回的对象的内容挂载到 父组件的 ​ref.current​ 上。

​forwardRef​​​会创建一个​​React​​​组件,这个组件能够将其接受的 ​​ref​​ 属性转发到其组件树下的另一个组件中。

import React, { forwardRef, useImperativeHandle, useEffect, useRef } from 'react'

const TestRef = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
open() {
console.log("open")
}
}))
})

function App () {
const ref = useRef()
useEffect(() => {
ref.current.open()
},[])

return(
<>
<div>111</div>
<TestRef ref={ref}></TestRef>
</>
)
}
export default App

使用useImperativeHandle可以透传 Ref,因为函数组件没有实例,所以在默认自定义函数组件中不能使用ref属性,使用为了解决此问题,react提供了一个hook和一个高阶组件完帮助函数组件能够使用ref属性。

[react] hook_前端_39

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S00ttDo3-1640594350545)(C:\Users\63062\AppData\Roaming\Typora\typora-user-images\image-20211223220537266.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zvSzUXgy-1640594350549)(C:\Users\63062\AppData\Roaming\Typora\typora-user-images\image-20211223220544407.png)]

11. useLayoutEffect

大部分情况下,使用 useEffect 就可以帮我们处理组件的副作用,但是如果想要同步调用一些副作用,比如对 DOM 的操作,就需要使用 useLayoutEffect,useLayoutEffect 中的副作用会在 DOM 更新之后同步执行。

[react] hook_数据_40

12. react-redux-hook

react-redux支持了hook的解决方案,提供两个方法,很方便我们在项目去使用

同步

[react] hook_react.js_41

[react] hook_javascript_42

[react] hook_App_43

使用redux完成异步

[react] hook_react.js_44

[react] hook_react.js_45

[react] hook_javascript_46

[react] hook_App_47

[react] hook_数据_48

[react] hook_javascript_49

[react] hook_App_50

13. react-router-dom-hook

react-router-dom也提供了hook的解决方案

​import {useHistory,useLocation,useParams} from 'react-router-dom'​

[react] hook_数据_51

[react] hook_前端_52

[react] hook_react.js_53

[react] hook_react.js_54

14. 路由懒加载和拆分

[react] hook_数据_55

[react] hook_前端_56




参考:
​React的State Hook用法详解!