美文网首页React上路啦
react脚手架,redux,hooks等概述

react脚手架,redux,hooks等概述

作者: dong_1122 | 来源:发表于2021-12-10 14:19 被阅读0次

    1. react脚手架

    1.1 说明

    1. xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目
      a. 包含了所有需要的配置(语法检查、jsx编译、devServer…)
      b. 下载好了所有相关的依赖
      c. 可以直接运行一个简单效果
    2. react提供了一个用于创建react项目的脚手架库: create-react-app
    3. 项目的整体技术架构为: react + webpack + es6 + eslint
    4. 使用脚手架开发的项目的特点: 模块化, 组件化, 工程化

    1.2 使用create-react-app创建react应用

    1. 全局安装:npm install -g create-react-app
    2. 切换到想创项目的目录,使用命令:create-react-app hello-react
    3. 进入项目文件夹:cd hello-react
    4. 启动项目:npm start

    1.3 react脚手架项目结构


    WeChatb9668a08acdb572e5c5b95f06ffab1e5.png

    1.4 项目文件介绍

    public ---- 静态资源文件夹
        favicon.icon ------ 网站页签图标
        index.html -------- 主页面
        logo192.png ------- logo图
        logo512.png ------- logo图
        manifest.json ----- 应用加壳的配置文件
        robots.txt -------- 爬虫协议文件
    src ---- 源码文件夹
        App.css -------- App组件的样式
        App.js --------- App组件
        App.test.js ---- 用于给App做测试
        index.css ------ 样式
        index.js ------- 入口文件
        logo.svg ------- logo图
        reportWebVitals.js --- 页面性能分析文件(需要web-vitals库的支持)
        setupTests.js ---- 组件单元测试的文件(需要jest-dom库的支持)
    

    2. redux使用

    2.1 学习文档
    1. 英文文档
    2. 中文文档
    3. Github

    2.2 理解

    1. redux是一个专门用于做状态管理的JS库(不是react插件库)。
    2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用。
    3. 作用: 集中式管理react应用中多个组件共享的状态。

    2.3 工作流程


    redux原理图.png

    2.4 redux 三个核心概念

    1. action
      a. 动作对象
      b. 包含两个属性
      type: 标识属性,值为字符串,唯一
      data: 数据属性,值类型任意,可选属性
      c. 举例: {type:'increment',data: 'hello world!'}

    2. reducer
      2.1 用于初始化状态,加工状态
      2.2 加工时,根据旧的state和action,产生新的state的纯函数

    3. store
      3.1 将state,action,reducer联系在一起的对象
      3.2 如何得到此对象?

    1) import {createStore} from 'redux'
    2) import reducer from './reducers'
    3) const store = createStore(reducer)
    

    3.3 此对象的作用?

    1. getState(): 得到state
    2. dispatch(action): 分发action, 触发reducer调用, 产生新的state
    3. subscribe(listener): 注册监听, 当产生了新的state时, 自动调用

    2.5 使用redux编写应用
    文件目录


    WechatIMG160.png
    // src/index.js
    
    import React from 'react'
    import ReactDOM from 'react-dom'
    import App from './App'
    import store from './redux/store'
    
    ReactDOM.render(<App/>,document.getElementById('root'))
    
    store.subscribe(()=>{
        ReactDOM.render(<App/>,document.getElementById('root'))
    })
    
    // src/App.jsx
    
    import React, { Component } from 'react'
    import Count from './components/Count'
    
    export default class App extends Component {
        render() {
            return (
                <div>
                    <Count/>
                </div>
            )
        }
    }
    
    // src/component/Count/index.jsx
    
    import React, { Component } from 'react'
    //引入store,用于获取redux中保存状态
    import store from '../../redux/store'
    //引入actionCreator,专门用于创建action对象
    import {
        createIncrementAction,
        createDecrementAction,
        createIncrementAsyncAction
    } from '../../redux/count_action'
    
    export default class Count extends Component {
    
        state = {carName:'奔驰c63'}
    
        /* componentDidMount(){
            //检测redux中状态的变化,只要变化,就调用render
            store.subscribe(()=>{
                this.setState({})
            })
        } */
    
        //加法
        increment = ()=>{
            const {value} = this.selectNumber
            store.dispatch(createIncrementAction(value*1))
        }
        //减法
        decrement = ()=>{
            const {value} = this.selectNumber
            store.dispatch(createDecrementAction(value*1))
        }
        //奇数再加
        incrementIfOdd = ()=>{
            const {value} = this.selectNumber
            const count = store.getState()
            if(count % 2 !== 0){
                store.dispatch(createIncrementAction(value*1))
            }
        }
        //异步加
        incrementAsync = ()=>{
            const {value} = this.selectNumber
            // setTimeout(()=>{
                store.dispatch(createIncrementAsyncAction(value*1,500))
            // },500)
        }
    
        render() {
            return (
                <div>
                    <h1>当前求和为:{store.getState()}</h1>
                    <select ref={c => this.selectNumber = c}>
                        <option value="1">1</option>
                        <option value="2">2</option>
                        <option value="3">3</option>
                    </select>&nbsp;
                    <button onClick={this.increment}>+</button>&nbsp;
                    <button onClick={this.decrement}>-</button>&nbsp;
                    <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
                    <button onClick={this.incrementAsync}>异步加</button>&nbsp;
                </div>
            )
        }
    }
    
    // src/redux/store.js
    
    /* 
        该文件专门用于暴露一个store对象,整个应用只有一个store对象
    */
    
    //引入createStore,专门用于创建redux中最为核心的store对象
    import {createStore,applyMiddleware} from 'redux'
    //引入为Count组件服务的reducer
    import countReducer from './count_reducer'
    //引入redux-thunk,用于支持异步action
    import thunk from 'redux-thunk'
    //暴露store
    export default createStore(countReducer,applyMiddleware(thunk))
    
    // src/redux/constant.js
    
    /* 
        该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
    */
    export const INCREMENT = 'increment'
    export const DECREMENT = 'decrement'
    
    // src/redux/count_action.js
    
    /* 
        该文件专门为Count组件生成action对象
    */
    import {INCREMENT,DECREMENT} from './constant'
    
    export const createIncrementAction = data => ({type:INCREMENT,data})
    export const createDecrementAction = data => ({type:DECREMENT,data})
    
    //异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
    export const createIncrementAsyncAction = (data,time) => {
        return (dispatch)=>{
            setTimeout(()=>{
                dispatch(createIncrementAction(data))
            },time)
        }
    }
    
    // scr/redux/count_reducer.js
    
    /* 
        1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
        2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
    */
    import {INCREMENT,DECREMENT} from './constant'
    
    const initState = 0 //初始化状态
    export default function countReducer(preState=initState,action){
        // console.log(preState);
        //从action对象中获取:type、data
        const {type,data} = action
        //根据type决定如何加工数据
        switch (type) {
            case INCREMENT: //如果是加
                return preState + data
            case DECREMENT: //若果是减
                return preState - data
            default:
                return preState
        }
    }
    

    3. react-redux使用

    1. 理解
      1.1 一个react插件库
      1.2 专门用来简化react应用中使用redux

    2. 工作流程


      react-redux模型图.png
    3. react-Redux将所有组件分成两大类
      3.1. UI组件

    1. 只负责 UI 的呈现,不带有任何业务逻辑
    2. 通过props接收数据(一般数据和函数)
    3. 不使用任何 Redux 的 API

    3.2. 容器组件

    1. 负责管理数据和业务逻辑,不负责UI的呈现
    2. 使用 Redux 的 API
    1. 相关API
      4.1 Provider:让所有组件都可以得到state数据
    import {Provider} from 'react-redux'
    
    ReactDOM.render(
        /* 此处需要用Provider包裹App,目的是让App所有的后代容器组件都能接收到store */
        <Provider store={store}>
            <App/>
        </Provider>,
        document.getElementById('root')
    )
    

    4.2 connect:用于包装 UI 组件生成容器组件

    import {connect} from 'react-redux'
    export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
    

    4.3 mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性

    function mapStateToProps(state){
        return {count:state}
    }
    

    4.4 mapDispatchToProps:将分发action的函数转换为UI组件的标签属性

    function mapDispatchToProps(dispatch){
        return {
            jia:number => dispatch(createIncrementAction(number)),
            jian:number => dispatch(createDecrementAction(number)),
            jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
        }
    }
    
    1. 使用react-redux编写应用
      文件目录


      WechatIMG527.png
    // src/index.js
    
    import React from 'react'
    import ReactDOM from 'react-dom'
    import App from './App'
    import store from './redux/store'
    import {Provider} from 'react-redux'
    
    ReactDOM.render(
        /* 此处需要用Provider包裹App,目的是让App所有的后代容器组件都能接收到store */
        <Provider store={store}>
            <App/>
        </Provider>,
        document.getElementById('root')
    )
    
    // src/App.jsx
    
    import React, { Component } from 'react'
    import Count from './containers/Count' //引入的Count的容器组件
    import Person from './containers/Person' //引入的Person的容器组件
    
    export default class App extends Component {
        render() {
            return (
                <div>
                    <Count/>
                    <hr/>
                    <Person/>
                </div>
            )
        }
    }
    
    // src/redux/store.js
    
    /* 
        该文件专门用于暴露一个store对象,整个应用只有一个store对象
    */
    
    //引入createStore,专门用于创建redux中最为核心的store对象
    import {createStore,applyMiddleware} from 'redux'
    //引入汇总之后的reducer
    import reducer from './reducers'
    //引入redux-thunk,用于支持异步action
    import thunk from 'redux-thunk'
    //引入redux-devtools-extension
    import {composeWithDevTools} from 'redux-devtools-extension'
    
    //暴露store 
    export default createStore(reducer,composeWithDevTools(applyMiddleware(thunk)))
    
    // src/redux/constant.js
    
    /* 
        该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
    */
    export const INCREMENT = 'increment'
    export const DECREMENT = 'decrement'
    export const ADD_PERSON = 'add_person'
    
    // src/redux/reducers/index.jsx
    
    /* 
        该文件用于汇总所有的reducer为一个总的reducer
    */
    //引入combineReducers,用于汇总多个reducer
    import {combineReducers} from 'redux'
    //引入为Count组件服务的reducer
    import count from './count'
    //引入为Person组件服务的reducer
    import persons from './person'
    
    //汇总所有的reducer变为一个总的reducer
    export default  combineReducers({
        count,
        persons
    })
    
    // src/redux/reducers/person.js
    
    import {ADD_PERSON} from '../constant'
    
    //初始化人的列表
    const initState = [{id:'001',name:'tom',age:18}]
    
    export default function personReducer(preState=initState,action){
        // console.log('personReducer@#@#@#');
        const {type,data} = action
        switch (type) {
            case ADD_PERSON: //若是添加一个人
                //preState.unshift(data) //此处不可以这样写,这样会导致preState被改写了,personReducer就不是纯函数了。
                return [data,...preState]
            default:
                return preState
        }
    }
    
    // src/redux/reducers/count.js
    
    /* 
        1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
        2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
    */
    import {INCREMENT,DECREMENT} from '../constant'
    
    const initState = 0 //初始化状态
    export default function countReducer(preState=initState,action){
        // console.log('countReducer@#@#@#');
        //从action对象中获取:type、data
        const {type,data} = action
        //根据type决定如何加工数据
        switch (type) {
            case INCREMENT: //如果是加
                return preState + data
            case DECREMENT: //若果是减
                return preState - data
            default:
                return preState
        }
    }
    
    // src/actions/person.js
    
    import {ADD_PERSON} from '../constant'
    
    //创建增加一个人的action动作对象
    export const addPerson = personObj => ({type:ADD_PERSON,data:personObj})
    
    // src/actions/count.js
    
    /* 
        该文件专门为Count组件生成action对象
    */
    import {INCREMENT,DECREMENT} from '../constant'
    
    //同步action,就是指action的值为Object类型的一般对象
    export const increment = data => ({type:INCREMENT,data})
    export const decrement = data => ({type:DECREMENT,data})
    
    //异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
    export const incrementAsync = (data,time) => {
        return (dispatch)=>{
            setTimeout(()=>{
                dispatch(increment(data))
            },time)
        }
    }
    
    //src/containers/Count/index.jsx
    
    import React, { Component } from 'react'
    //引入action
    import {
        increment,
        decrement,
        incrementAsync
    } from '../../redux/actions/count'
    //引入connect用于连接UI组件与redux
    import {connect} from 'react-redux'
    
    //定义UI组件
    class Count extends Component {
    
        state = {carName:'奔驰c63'}
    
        //加法
        increment = ()=>{
            const {value} = this.selectNumber
            this.props.increment(value*1)
        }
        //减法
        decrement = ()=>{
            const {value} = this.selectNumber
            this.props.decrement(value*1)
        }
        //奇数再加
        incrementIfOdd = ()=>{
            const {value} = this.selectNumber
            if(this.props.count % 2 !== 0){
                this.props.increment(value*1)
            }
        }
        //异步加
        incrementAsync = ()=>{
            const {value} = this.selectNumber
            this.props.incrementAsync(value*1,500)
        }
    
        render() {
            //console.log('UI组件接收到的props是',this.props);
            return (
                <div>
                    <h2>我是Count组件,下方组件总人数为:{this.props.renshu}</h2>
                    <h4>当前求和为:{this.props.count}</h4>
                    <select ref={c => this.selectNumber = c}>
                        <option value="1">1</option>
                        <option value="2">2</option>
                        <option value="3">3</option>
                    </select>&nbsp;
                    <button onClick={this.increment}>+</button>&nbsp;
                    <button onClick={this.decrement}>-</button>&nbsp;
                    <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
                    <button onClick={this.incrementAsync}>异步加</button>&nbsp;
                </div>
            )
        }
    }
    
    //使用connect()()创建并暴露一个Count的容器组件
    export default connect(
        state => ({
            count:state.count,
            personCount:state.persons.length
        }),
        {increment,decrement,incrementAsync}
    )(Count)
    
    // src/containers/Person/index.jsx
    
    import React, { Component } from 'react'
    import {nanoid} from 'nanoid'
    import {connect} from 'react-redux'
    import {addPerson} from '../../redux/actions/person'
    
    class Person extends Component {
    
        addPerson = ()=>{
            const name = this.nameNode.value
            const age = this.ageNode.value*1
            const personObj = {id:nanoid(),name,age}
            this.props.addPerson(personObj)
            this.nameNode.value = ''
            this.ageNode.value = ''
        }
    
        render() {
            return (
                <div>
                    <h2>我是Person组件,上方组件求和为{this.props.count}</h2>
                    <input ref={c=>this.nameNode = c} type="text" placeholder="输入名字"/>
                    <input ref={c=>this.ageNode = c} type="text" placeholder="输入年龄"/>
                    <button onClick={this.addPerson}>添加</button>
                    <ul>
                        {
                            this.props.persons.map((p)=>{
                                return <li key={p.id}>{p.name}--{p.age}</li>
                            })
                        }
                    </ul>
                </div>
            )
        }
    }
    
    export default connect(
        state => ({
            persons:state.persons,
            count:state.count
        }),//映射状态
        {addPerson}//映射操作状态的方法
    )(Person)
    

    3. hooks使用

    理解:react 16.8 的新增特性。可以让你在不编写 class 的情况下使用 state 以及其他的 react 特性。

    1. useState

    返回一个 state,以及更新 state 的函数

    注意事项:

    1. 无局部更新能力
    2. 如果state是一个对象,能否部分setState?答案是不行,因为setState不会帮我们合并属性
    3. setState(obj) 如果obj地址不变,那么React就认为数据没有变化,不会更新视图。所以如果传入setState()是一个引用类型,地址要进行改变。
    function App(){
        const [user, setUser] = React.useState({name: 'Jack', age: 18})
        const onClick = () =>{
            //setUser不可以局部更新,如果只改变其中一个,那么整个数据都会被覆盖
            // setUser({
            //  name: 'Frank'
            // })
            setUser({
                ...user, //拷贝之前的所有属性
                name: 'Frank' //这里的name覆盖之前的name
            })
        }
        return (
            <div className='App'>
                <h1>{user.name}</h1>
                <h2>{user.age}</h2>
                <button onClick={onClick}>Click</button>
            </div>
        )
    }
    

    setState也可以接受一个回调函数,该函数将接收先前的 state,并返回一个更新后的值

    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>
        </>
      );
    }
    

    2. useContext

    useContext可以很方便的去订阅 context 的改变,并在合适的时候重新渲染组件

    标准的 context API 用法:

    const ThemeContext = React.createContext('light');
    // 只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效
    
    class App extends React.Component {
      render() {
        return (
          <ThemeContext.Provider value="dark">
            <Toolbar />
          </ThemeContext.Provider>
        );
      }
    }
    
    // 中间层组件
    function Toolbar(props) {
      return (
        <div>
          <ThemedButton />
        </div>
      );
    }
    
    class ThemedButton extends React.Component {
      // 通过定义静态属性 contextType 来订阅
      static contextType = ThemeContext;
      render() {
        return <Button theme={this.context} />;
      }
    }
    

    除了定义静态属性的方式,还有另外一种针对Function Component的订阅方式

    function ThemedButton() {
        // 通过定义 Consumer 来订阅
        return (
            <ThemeContext.Consumer>
              {value => <Button theme={value} />}
            </ThemeContext.Consumer>
        );
    }
    

    使用useContext来订阅,代码会是这个样子,没有额外的层级和奇怪的模式:

    function ThemedButton() {
      const value = useContext(ThemeContext);
      return <Button theme={value} />;
    }
    // useContext(ThemeContext) 相当于 class 组件中的 static contextType = ThemeContext 或<ThemeContext.Consumer>
    

    3. useReducer

    useState的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。

    可以用来用来解决复杂结构的state和state处理逻辑比较复杂的情况

    const initialState = {count: 0};
    
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return {count: state.count + 1};
        case 'decrement':
          return {count: state.count - 1};
        default:
          throw new Error();
      }
    }
    
    function Counter() {
      const [state, dispatch] = useReducer(reducer, initialState);
      return (
        <>
          Count: {state.count}
          <button onClick={() => dispatch({type: 'decrement'})}>-</button>
          <button onClick={() => dispatch({type: 'increment'})}>+</button>
        </>
      );
    }
    

    惰性初始化

    将init 函数作为 useReducer 的第三个参数传入,这样初始 state 将被设置为 init(initialArg)。这么做可以将用于计算 state 的逻辑提取到 reducer 外部,这也为将来对重置 state 的 action 做处理提供了便利:

    function init(initialCount) {  return {count: initialCount};}
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return {count: state.count + 1};
        case 'decrement':
          return {count: state.count - 1};
        case 'reset':      return init(action.payload);    default:
          throw new Error();
      }
    }
    
    function Counter({initialCount}) {
      const [state, dispatch] = useReducer(reducer, initialCount, init);  return (
        <>
          Count: {state.count}
          <button
            onClick={() => dispatch({type: 'reset', payload: initialCount})}>Reset
          </button>
          <button onClick={() => dispatch({type: 'decrement'})}>-</button>
          <button onClick={() => dispatch({type: 'increment'})}>+</button>
        </>
      );
    }
    

    4. useEffect 副作用

    1. 该hook 接收一个包含命令式、且可能有副作用代码的函数。
      可以在该函数中执行一些副作用操作,例如:
      a. 发ajax请求数据获取
      b. 设置订阅 / 启动定时器
      c. 手动更改真实DOM

    2. 可以把 useEffect Hook 简单的看做如下三个函数的组合(用于模拟类组件中的生命周期钩子)
      componentDidMount() —— [] 指定空数组作第二个参数
      componentDidUpdate() —— [count] 数组中可指定依赖项,只有当count发生变化,才会执行effect hook
      componentWillUnmount() —— 通过 return 函数方式

    function Demo(){
        const [count,setCount] = React.useState(0)
    
        React.useEffect(()=>{
            console.log('effect');
            let timer = setInterval(()=>{
                setCount(count => count+1 )
            },1000)
            return ()=>{
                clearInterval(timer)
            }
        },[])
        // 指定空数组作为依赖项,页面渲染完成后,只执行一次。即使count发生变化,也不会再执行effect hook。
        // return 一个函数,组件销毁时执行,清除计时器
    
        //加的回调
        function add(){
            //setCount(count+1) //第一种写法
            setCount(count => count+1 )
        }
    
        //卸载组件的回调
        function unmount(){
            ReactDOM.unmountComponentAtNode(document.getElementById('root'))
        }
    
        return (
            <div>
                <input type="text" ref={myRef}/>
                <h2>当前求和为:{count}</h2>
                <button onClick={add}>点我+1</button>
                <button onClick={unmount}>卸载组件</button>
            </div>
        )
    }
    

    如果需要监听count的变化,执行一些操作,那么可以将count记录为依赖项。count发生改变,effect hook会再次执行。

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

    effect 的执行时机

    与 componentDidMount、componentDidUpdate 不同的是,在浏览器完成布局与绘制之后,传给 useEffect 的函数会延迟调用。这使得它适用于许多常见的副作用场景,比如设置订阅和事件处理等情况,因此不应在函数中执行阻塞浏览器更新屏幕的操作。

    然而,并非所有 effect 都可以被延迟执行。例如,在浏览器执行下一次绘制前,用户可见的 DOM 变更就必须同步执行,这样用户才不会感觉到视觉上的不一致。(概念上类似于被动监听事件和主动监听事件的区别。)

    5. useRef

    返回一个可变的 ref 对象

    1. Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
    2. 语法: const refContainer = useRef()
    3. 作用:保存标签对象,功能与React.createRef()一样
    function TextInputWithFocusButton() {
      const inputEl = useRef(null);
      const onButtonClick = () => {
        // `current` 指向已挂载到 DOM 上的文本输入元素
        inputEl.current.focus();
      };
      return (
        <>
          <input ref={inputEl} type="text" />
          <button onClick={onButtonClick}>Focus the input</button>
        </>
      );
    }
    

    6. useCallback / useMemo

    useCallback和useMemo设计的初衷是用来做性能优化的。

    在Class Component中考虑以下的场景

    class Foo extends Component {
      handleClick() {
        console.log('Click happened');
      }
      render() {
        return <Button onClick={() => this.handleClick()}>Click Me</Button>;
      }
    }
    

    传给 Button 的 onClick 方法每次都是重新创建的,这会导致每次 Foo render 的时候,Button 也跟着 render。优化方法有 2 种,箭头函数和 bind。下面以 bind 为例子:

    class Foo extends Component {
      constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
      }
      handleClick() {
        console.log('Click happened');
      }
      render() {
        return <Button onClick={this.handleClick}>Click Me</Button>;
      }
    }
    

    同样的,Function Component也有这个问题:

    function Foo() {
      const [count, setCount] = useState(0);
    
      const handleClick() {
        console.log(`Click happened with dependency: ${count}`)
      }
      return <Button onClick={handleClick}>Click Me</Button>;
    }
    

    React 给出的方案是useCallback Hook。在依赖不变的情况下,它会返回相同的引用,避免子组件进行无意义的重复渲染:

    function Foo() {
      const [count, setCount] = useState(0);
    
      const memoizedHandleClick = useCallback(
        () => console.log(`Click happened with dependency: ${count}`), [count],
      ); 
      return <Button onClick={memoizedHandleClick}>Click Me</Button>;
    }
    

    useCallback缓存的是方法的引用,而useMemo缓存的则是方法的返回值。使用场景是减少不必要的子组件渲染:

    function Parent({ a, b }) {
      // 当 a 改变时才会重新渲染
      const child1 = useMemo(() => <Child1 a={a} />, [a]);
      // 当 b 改变时才会重新渲染
      const child2 = useMemo(() => <Child2 b={b} />, [b]);
      return (
        <>
          {child1}
          {child2}
        </>
      )
    }
    

    相关文章

      网友评论

        本文标题:react脚手架,redux,hooks等概述

        本文链接:https://www.haomeiwen.com/subject/vppcfrtx.html