我们先来思考一下class组件相对于函数式组件有什么优势?比较常见的是下面的优势:
所以,在Hook出现之前,对于上面这些情况我们通常都会编写class组件。
复杂组件变得难以理解:
Hook的出现,可以解决上面提到的这些问题;
简单总结一下hooks:
那么我们来研究一下核心的一段代码代表什么意思:
useState来自react,需要从react中导入,它是一个hook;
React 使用 Object.is 比较算法 来比较 state。
import { memo, useState } from "react";function CounterHook(props) {const [counter, setCounter] = useState(0)console.log(name)return (当前计数: {counter}
)
}
export default memo(CounterHook)
Hook 就是 JavaScript 函数,这个函数可以帮助你 钩入(hook into) React State以及生命周期等特性;
但是使用它们会有两个额外的规则:
State Hook的API就是 useState,我们在前面已经进行了学习:
目前我们已经通过hook在函数式组件中定义state,那么类似于生命周期这些呢?
useEffect的解析:
useEffect()是一个钩子函数,需要一个函数作为参数
这个作为参数的函数,将会在组件渲染完毕后执行
在开发中,可以将那些会产生副作用的代码编写到useEffect的回调函数中 例如fetch触发刷新的方法调用在useEffect fetch逻辑在useCallback
这样就可以避免这些代码影响到组件的渲染
默认情况下,useEffect()中的函数,会在组件渲染完成后调用,
并且是每次渲染完成后都会调用
在useEffect()可以传递一个第二个参数,
第二个参数是一个数组,在数组中可以指定Effect的依赖项
指定后,只有当依赖发生变化时,Effect才会被触发
通常会将Effect中使用的所有的局部变量都设置为依赖项
这样一来可以确保这些值发生变化时,会触发Effect的执行
像setState()是由钩子函数useState()生成的
useState()会确保组件的每次渲染都会获取到相同setState()对象
所以setState()方法可以不设置到依赖中
如果依赖项设置了一个空数组,则意味Effect只会在组件初始化时触发一次
在class组件的编写过程中,某些副作用的代码,我们需要在componentWillUnmount中进行清除:
useEffect传入的回调函数A本身可以有一个返回值,这个返回值是另外一个回调函数B:
为什么要在 effect 中返回一个函数?
总结:在Effect的回调函数中,可以指定一个函数作为返回值
这个函数可以称其为清理函数,它会在下次Effect执行前调用
可以在这个函数中,做一些工作来清除上次Effect执行所带来的的影响
使用Hook的其中一个目的就是解决class中生命周期经常将很多的逻辑放在一起的问题:
import React, { memo, useEffect } from 'react'
import { useState } from 'react'const App = memo(() => {const [count, setCount] = useState(0)// 负责告知react, 在执行完当前组件渲染之后要执行的副作用代码useEffect(() => {// 1.修改document的title(1行)console.log("修改title")})// 一个函数式组件中, 可以存在多个useEffectuseEffect(() => {console.log("监听redux中的数据")return () => {// 取消redux中数据的监听}})useEffect(() => {console.log("监听eventBus的事件")return () => {// 取消eventBus中的事件监听}})return ()
})export default App
Hook 允许我们按照代码的用途分离它们, 而不是像生命周期函数那样:
默认情况下,useEffect的回调函数会在每次渲染时都重新执行,但是这会导致两个问题:
但是,如果一个函数我们不希望依赖任何的内容时,也可以传入一个空的数组 []:
import React, { memo, useEffect } from 'react'
import { useState } from 'react'const App = memo(() => {const [count, setCount] = useState(0)const [message, setMessage] = useState("Hello World")useEffect(() => {console.log("修改title:", count)}, [count])useEffect(() => {console.log("监听redux中的数据")return () => {}}, [])useEffect(() => {console.log("监听eventBus的事件")return () => {}}, [])useEffect(() => {console.log("发送网络请求, 从服务器获取数据")return () => {console.log("会在组件被卸载时, 才会执行一次")}}, [])return ()
})export default App
在之前的开发中,我们要在组件中使用共享的Context有两种方式:
import React, { memo, useContext } from 'react'
import { UserContext, ThemeContext } from "./context"const App = memo(() => {// 使用Contextconst user = useContext(UserContext)const theme = useContext(ThemeContext)return (User: {user.name}-{user.level}
{color: theme.color, fontSize: theme.size}}>Theme
)
})export default App
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { UserContext, TokenContext } from "./context"
import store from './store';const root = ReactDOM.createRoot(document.getElementById('root'));
root.render({name: "kobe", level: 99}}>
);
useReducer仅仅是useState的一种替代方案:
import React, { memo, useReducer } from 'react'
function reducer(state, action) {switch(action.type) {case "increment":return { ...state, counter: state.counter + 1 }case "decrement":return { ...state, counter: state.counter - 1 }case "add_number":return { ...state, counter: state.counter + action.num }case "sub_number":return { ...state, counter: state.counter - action.num }default:return state}
}// useReducer+Context => reduxconst App = memo(() => {const [state, dispatch] = useReducer(reducer, { counter: 0, friends: [], user: {} })return (当前计数: {state.counter}
)
})export default App
reducer : 整合函数
import React, {useReducer, useState} from 'react';// 为了避免reducer会重复创建,通常reducer会定义到组件的外部
const countReducer = (state, action) => {switch (action.type) {case 'ADD':return state + 1;case 'SUB':return state - 1;default:return state;}
};const App = () => {const [count, countDispatch] = useReducer(countReducer, 1);const addHandler = () => {// 增加count的值countDispatch({type: 'ADD'});};const subHandler = () => {// 增加count的值countDispatch({type: 'SUB'});};return ({fontSize: 30, width: 200, height: 200, margin: '100px auto', textAlign: 'center'}}>{count});}
;export default App;
useCallback实际的目的是为了进行性能的优化。
如何进行性能的优化
通常使用useCallback的目的是不希望子组件进行多次渲染,并不是为了函数进行缓存;
当需要将一个函数传递给子组件时, 最好使用useCallback进行优化, 将优化之后的函数, 传递给子组件
参数:
useCallback 创建的回调函数不会总在组件重新渲染时重新创建
import React, {useCallback, useState} from 'react';
import A from "./components/A";const App = () => {console.log('App渲染');const [count, setCount] = useState(1);const [num, setNum] = useState(1);const clickHandler = useCallback(() => {setCount(prevState => prevState + num);setNum(prevState => num + 1);}, [num]);return (App -- {count}
clickHandler}/>);
};export default App;
useMemo实际的目的也是为了进行性能的优化。
如何进行性能的优化
总结:useMemo和useCallback这两个hooks都返回缓存的值,useMemo返回缓存的变量,useCallback返回缓存的函数。
import React, { memo, useCallback } from 'react'
import { useMemo, useState } from 'react'const HelloWorld = memo(function(props) {console.log("HelloWorld被渲染~")return Hello World
})function calcNumTotal(num) {let total = 0for (let i = 1; i <= num; i++) {total += i}return total
}const App = memo(() => {const [count, setCount] = useState(0)// useMemo和useCallback的对比function fn() {}// const increment = useCallback(fn, [])// const increment2 = useMemo(() => fn, [])const info = useMemo(() => ({name: "why", age: 18}), [])return (计算结果: {result}
计数器: {count}
result} info={info} /> )
})export default App
下一篇:python练习题5.0