React-Hooks笔记
2026/8/5 10:39:56 网站建设 项目流程

class组件存在的问题和添加Hooks的原因

一、函数组件的特点

  • 没有组件实例
  • 没有生命周期
  • 没有state和setState,只能接收props

二、class组件的问题

  • 大型组件很难拆分和重构,很难测试(即class不易拆分)
  • 相同业务逻辑,分散到各个方法中,逻辑混乱
  • 复用逻辑变得复杂,如:Mixins,HOC,Render Prop

三、React组件更易用函数表达

  • React提倡函数式编程
  • 函数更灵活,更易拆分,更易测试
  • 但是函数组件太简单,需要增强能力-----Hooks

Hooks的具体讲解

1、State Hook

注意:让函数组件实现state和setState

  • 默认函数组件没有state
  • 函数组件是一个纯函数,执行完即销毁,无法存储state
  • 需要State Hook ,即把state功能 “钩” 到纯函数中

1、函数组件:

import React from 'react' import { useState } from 'react'; function ClickCount() { const [count, setCount] = useState(0); return ( <div> <p>你点击了{count}次</p> <button onClick={() => setCount(count + 1)}>点击</button> </div> ) } export default ClickCount;

2、class组件:

import React from 'react' class ClickCountClass extends React.Component { constructor(props) { super(props); this.state = { count: 0 } this.handleClick = this.handleClick.bind(this); } render() { return <div> <p>你点击了{this.state.count}次</p> <button onClick={this.handleClick}>点击</button> </div> } handleClick() { this.setState({ count: this.state.count + 1 }) } } export default ClickCountClass;

3、useState使用总结

  • useState(0)传入初始值,返回数组[state,setSate]
  • 通过state获取值
  • 通过setState(1)修改值

4、Hooks命名规范

规定所有Hooks都用use开头,如:useXXX

自定义Hooks也要用use开头

非Hooks的地方,尽量不要用useXXX写法

Effect Hook具体讲解

1、让函数组件模拟生命周期

  • 默认函数组件没有生命周期
  • 函数组件是一个纯函数,执行完即销毁,自己无法实现生命周期
  • 使用Effect Hook把生命周期“钩”到纯函数中

2、模拟class组件的DidMount和DidUpdata

useEffect(() => { console.log('name', name); });

3、模拟class组件的DidMount

useEffect(() => { console.log('组件挂载'); }, []);

4、模拟class组件的DidUpdata

useEffect(() => { console.log('count', count, 'name', name); }, [count, name]);

注意:只有写在"【】"中间的元素变化更新了才会触发DidUpdata,如果元素没有写在这里,那么就算元素改变了,也不会触发DidUpdata

5、useEffect总结

  • 模拟DidMount --- useEffect依赖[]
  • 模拟DidUpdata --- useEffect无依赖,或者依赖 [a, b]
  • 模拟WillUnMount --- useEffect 中返回一个函数

6、useEffect让纯函数有了副作用

  • 默认情况下,执行纯函数,输入参数,返回结果,无副作用
  • 所谓副作用,就是对函数之外造成影响,如设置全局定时任务
  • 而组件需要副作用,所以需要useEffect “钩” 入纯函数中

注意:useEffect可能会出现内存泄漏的问题,比如纯函数的Effect中写了定时任务,没有做销毁,而外部又将纯函数销毁了,那么定时器不会自动关闭,还会继续存在。

useRef具体讲解

注意:主要用于获取Dom节点,通过.current调用

import React, { useRef, useEffect } from 'react'; function UseFef() { const inputRef = useRef(null); useEffect(()=>{ console.log(inputRef.current);//Dom节点 },[]) return ( <div ref={inputRef}> 按钮 </div> ); } export default UseFef;

useContext具体讲解

注意:React中的Context具体意义就是全局存值。可以理解为vuex或者Rudex的简单版本

import React, { useContext } from 'react'; //主题颜色 const Themes = { light: { foreground: '#000', background: '#fff' }, dark: { foreground: '#fff', background: '#222' } } const ThemeContext = React.createContext(Themes.light); function ThemedButton() {//底层组件 const theme = useContext(ThemeContext);//使用useContext获取主题颜色 return ( <button style={{ background: theme.background, color: theme.foreground }}>你好!</button> ) } function Toolbar() {//中间组件 return ( <ThemedButton /> ) } function App() {//顶层组件 return ( <ThemeContext.Provider value={Themes.dark}>//使用ThemeContext.Provider提供主题颜色 <Toolbar /> </ThemeContext.Provider> ) } export default App;

useReducer具体讲解

import React, { useReducer } from 'react'; const initialState = { count: 0 }; const reducer = (state: any, action: any) => { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: return state; } } function App() { //很像useState,const [count, setCount] = useState(0); const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}>Increment</button> <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button> </div> ) } export default App;

1、useReducer和redux的区别

  • useReducer是useState的代替方案,用于state的复杂变化
  • useReducer是单个组件的状态管理,组件通讯还是需要props
  • redux是全局的状态管理,多组件共享数据

useMemo的具体讲解

注意:用于Hooks性能优化的

1、有问题的情况

注意:我只改变父组件的数据情况下,子组件会跟着一起发生改变,去进行更新渲染。这样的情况是不应该的,因为我子组件没有数据变化,不应该让他重新渲染,这样会造成资源的浪费,耗费性能。

import React, { useMemo, useState } from 'react'; //子组件 function Child({ userInfo }) { console.log('Child组件渲染'); return ( <div> <p>Name: {userInfo.name}</p> <p>Age: {userInfo.age}</p> </div> ) } //父组件 function App() { console.log('父组件渲染'); const [count, setCount] = useState(0); const [name, setName] = useState('江停'); const userInfo = {name, age: 30}; return ( <div> <Child userInfo={userInfo} /> <button onClick={() => setCount(count + 1)}>Count: {count}</button> <button onClick={() => setName(name + '1')}>Name: {name}</button> </div> ) } export default App;

2、memo的应用

注意:虽然上述情况react会自己做nodeChild的校验,如果在没有影响性能的情况下,可以不用做memo的性能优化操作

注意:类似于PureComponent,对props进行浅层的比较

注意:用useMemo缓存数据,当name变化时,userInfoMemo会重新计算,[]表示依赖项,当依赖项变化时,useMemo会重新计算

import React, { memo, useMemo, useState } from 'react'; //子组件 const Child = memo(({ userInfo }: { userInfo: { name: string; age: number } }) => { console.log('Child组件渲染'); return ( <div> <p>Name: {userInfo.name}</p> <p>Age: {userInfo.age}</p> </div> ) }) //父组件 function App() { console.log('父组件渲染'); const [count, setCount] = useState(0); const [name, setName] = useState('江停'); const userInfo = {name, age: 30}; //用useMemo缓存数据,当name变化时,userInfoMemo会重新计算,[]表示依赖项,当依赖项变化时,useMemo会重新计算 const userInfoMemo = useMemo(() => userInfo, [name]); return ( <div> <Child userInfo={userInfoMemo} /> <button onClick={() => setCount(count + 1)}>Count: {count}</button> <button onClick={() => setName(name + '1')}>Name: {name}</button> </div> ) } export default App;

3、useMemo总结

  • React默认会更新所有子组件
  • class组件使用SUC和PureComponent做优化
  • Hooks中使用useMemo,但是优化的原理都是一样的

useCallback的具体讲解

1、有问题的情况

注意:当我们在子组件内添加一个传递的函数,之前的子组件又会自动更新。

import React, { memo, useMemo, useState } from 'react'; //子组件 const Child = memo(({ userInfo,onChange }: { userInfo: { name: string; age: number }, onChange: () => void }) => { console.log('Child组件渲染'); return ( <div> <p>Name: {userInfo.name}</p> <p>Age: {userInfo.age}</p> <button onClick={onChange}>Change</button> </div> ) }) //父组件 function App() { console.log('父组件渲染'); const [count, setCount] = useState(0); const [name, setName] = useState('江停'); const userInfo = {name, age: 30}; //用useMemo缓存数据,当name变化时,userInfoMemo会重新计算,[]表示依赖项,当依赖项变化时,useMemo会重新计算 const userInfoMemo = useMemo(() => userInfo, [name]); const onChange = (e: React.ChangeEvent<HTMLInputElement>) => { console.log('onChange',e.target.value); } return ( <div> <Child userInfo={userInfoMemo} onChange={(e: React.ChangeEvent<HTMLInputElement>) => onChange(e)} /> <button onClick={() => setCount(count + 1)}>Count: {count}</button> <button onClick={() => setName(name + '1')}>Name: {name}</button> </div> ) } export default App;

2、useCallback的封装

import React, { memo, useMemo, useState, useCallback } from 'react'; //子组件 const Child = memo(({ userInfo,onChange }: { userInfo: { name: string; age: number }, onChange: (e: any) => void }) => { console.log('Child组件渲染'); return ( <div> <p>Name: {userInfo.name}</p> <p>Age: {userInfo.age}</p> <button onClick={onChange}>Change</button> </div> ) }) //父组件 function App() { console.log('父组件渲染'); const [count, setCount] = useState(0); const [name, setName] = useState('江停'); const userInfo = {name, age: 30}; //用useMemo缓存数据,当name变化时,userInfoMemo会重新计算,[]表示依赖项,当依赖项变化时,useMemo会重新计算 const userInfoMemo = useMemo(() => userInfo, [name]); const onChange = useCallback((e) => { console.log('onChange',e.target.value); }, []); return ( <div> <Child userInfo={userInfoMemo} onChange={onChange} /> <button onClick={() => setCount(count + 1)}>Count: {count}</button> <button onClick={() => setName(name + '1')}>Name: {name}</button> </div> ) } export default App;

3、useCallback的总结

  • useMemo缓存数据
  • useCallback缓存函数,(但是传递的函数不要写成箭头函数,这样可能后导致useCallback失效)
  • 两者都是React Hooks的常见优化策略

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询