解决React中的re-render问题

目录
  • re-render?
  • 错误的优化
  • Object.is
  • 合理使用useEffect,解决re-render
  • 简单实现useEffect

文章转自公众号:前端巅峰

re-render?

首先使用我的脚手架:

npm i ykj-cli -g 
ykj init App
cd ./app
yarn 
yarn dev

这样一个webpack5、TS、React项目就搭建好了

我们目前只有一个APP组件,内部代码:

import Myy from './myy.jpg';

function App() {
    console.log('render')
    return (
        <div className="app">
            <h1>欢迎使用明源云 - 云空间前端通用脚手架</h1>
            <img src={Myy} alt="" style={{ width: 500, height: 500 }} />
            <h4>
                加入我们:<a>453089136@qq.com</a>
            </h4>
            <h4>微前端,webpack5,TypeScript,React,vite应有尽有</h4>
        </div>
    );
}

export default App;

刷新访问页面后,发现控制台只打印了一次

OK,那我们正式开始,加入一些状态,以及新增一个按钮和SubApp组件

import { useState } from 'react';
import Myy from './myy.jpg';
import SubApp from './SubApp'
function App() {
    const [state, setState] = useState("Peter");
    return (
        <div className="app">
            <h1>欢迎使用明源云 - 云空间前端通用脚手架</h1>
            <img src={Myy} alt="" style={{ width: 500, height: 500 }} />
            <h4>
                加入我们:<a>453089136@qq.com</a>
            </h4>
            <SubApp state={state} />
            <h4>微前端,webpack5,TypeScript,React,vite应有尽有</h4>
            <button onClick={() => {
                setState('关注公众号:前端巅峰')
            }}>测试按钮</button>
        </div>
    );
}

export default App;

//SubApp组件
function SubApp({ state }) {
    console.log('render')
    return <h1>{state}</h1>
}

export default SubApp

这个时候点击按钮,又触发了一次render

那么接下来我们继续点击按钮,看是否会继续render

多次点击,发现render只有一次,因为我们此时每次点击都是将state的值变成

那么,我们尝试着将state变成一个对象

const [state, setState] = useState({ des: "Peter" });
  <button onClick={() => {
                setState({ des: '关注公众号:前端巅峰' })
            }}>测试按钮</button>

神奇的事情发生了,这里每次点击设置同样的state,都会触发子组件render. - 这里就出现了re-render问题,我们其实都是拿的同样的state,但是却出现了不必要的render

错误的优化

很多同学在使用React过程中会错误的使用PureComponentuseEffect以及UseMemo.

这里我们先用错误的方式引入useEffect,改造SubApp

import React, { useEffect, useState } from 'react';

function SubApp({ state }) {
    const [data, setData] = useState({})
    useEffect(() => {
        console.log('render')
        setData(state)
    }, [state])
    console.log('render')
    return <h1>{data.des}</h1>
}

export default SubApp

这里可以看到,每次点击按钮都在不断render,使用了useEffect竟然失效了。这优化无效~

这里就要谈到useEffect的源码实现,其实useEffectPureComponent等一系列优化手段,大都是使用了Object.is()这个算法。

MDN资料地址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is

Object.is

Object.is() 方法判断两个值是否为同一个值。

value1
被比较的第一个值。
value2
被比较的第二个值。
返回值
一个 Boolean 类型标示两个参数是否是同一个值。

描述

Object.is() 方法判断两个值是否为同一个值。如果满足以下条件则两个值相等:

都是 undefined
都是 null
都是 true 或 false
都是相同长度的字符串且相同字符按相同顺序排列
都是相同对象(意味着每个对象有同一个引用)
都是数字且
都是 +0
都是 -0
都是 NaN
或都是非零而且非 NaN 且为同一个值
与== (en-US) 运算不同。== 运算符在判断相等前对两边的变量(如果它们不是同一类型) 进行强制转换 (这种行为的结果会将 "" == false 判断为 true), 而 Object.is不会强制转换两边的值。

与=== (en-US) 运算也不相同。=== 运算符 (也包括 == 运算符) 将数字 -0 和 +0 视为相等 ,而将Number.NaN 与NaN视为不相等.

合理使用useEffect,解决re-render

如果Props传入的是一个对象,那么由于React hooks每次更新都会生成一个全新的对象,而这个全新的对象和之前的对象,无法通过Object.is去对比,每次都会不相等。例如:

  const obj1 = {
            name: "peter"
        }
        const obj2 = {
            name: "peter"
        }

        console.log(Object.is(obj1, obj2, 'xx'))

这段代码永远输出都是false,因为obj1和obj2是两个不同地址的引用对象

但是如果我们换一种方式:

Object.is(obj1.name, obj2.name )

这样就可以正常比较,永远输出ture了

那么我们现在也要在useEffect中类似这样使用

    useEffect(() => {
        setData(state)
        console.log('render')
    }, [state.des])

这样我们的useEffect内部回调只会被触发一次

简单实现useEffect

网上抄的代码改造了比较方法:

let _deps;
function useEffect(callback, dependencies) {
  const hasChanged = _deps
    && !dependencies.every((el, i) =>Object.is( el ,_deps[i]))
    || true;
  // 如果 dependencies 不存在,或者 dependencies 有变化,就执行 callback
  if (!dependencies || hasChanged) {
    callback();
    _deps = dependencies;
  }
}

实现逻辑:将传入的依赖项遍历,通过Object.is方法与原依赖项的值进行浅对比,如果不一致就执行callback。

结语:
所以大家在解决re-render问题时,或者使用useEffect之类优化方案时,应该监听对比某个值,而不是去监听某个大对象进行对比。相信这篇文章能解决你心中疑惑很久的re-render问题,

到此这篇关于解决React中的re-render问题的文章就介绍到这了,更多相关React中的re-render问题内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 在react中使用highlight.js将页面上的代码高亮的方法

    通过 highlight.js 库实现对文章正文 HTML 中的代码元素自动添加语法高亮,highlight.js官方文档 下载highlight.js npm i highlight.js 导入highlight.js import hljs from 'highlight.js' import 'highlight.js/styles/vs2015.css'   用highlight.js   useEffect(() => {     // 配置 highlight.js     hljs

  • 如何利用React实现图片识别App

    先把效果图给大家放上来 个人觉得效果还行.识别不太准确是因为这个 app学习图片的时间太短(电脑太卡). (笔者是 window10) 安装运行环境: npm install --global windows-build-tools(这个时间很漫长...) npm install @tensorflow/tfjs-node(这个时间很漫长...) 项目目录如下 train文件夹 index.js(入口文件) const tf = require('@tensorflow/tfjs-node')

  • 解决React中的re-render问题

    目录 re-render? 错误的优化 Object.is 合理使用useEffect,解决re-render 简单实现useEffect 文章转自公众号:前端巅峰 re-render? 首先使用我的脚手架: npm i ykj-cli -g  ykj init App cd ./app yarn  yarn dev 这样一个webpack5.TS.React项目就搭建好了 我们目前只有一个APP组件,内部代码: import Myy from './myy.jpg'; function App

  • 解决react中useState状态异步更新的问题

    目录 疑惑 状态异步更新带来的问题 问题示例 问题解决 类组件的解决方案 函数组件的解决方案 其他解决方案 结尾 疑惑 相信刚开始使用react函数组件的小伙伴也遇到过一个坑,就是 useState 更新状态是异步更新的,但是react 并没有提供关于这个问题的解决方案.那我们能否使用自己的方法来解决这个问题呢?答案肯定是可以的. 状态异步更新带来的问题 就拿一个比较常见的场景来说.在react项目中,我们想在关闭对话框后再去处理其他业务.但是 useState 的状态是异步更新的.我们通过se

  • 解决react中label标签for报错问题

    要求:点击账号或密码,自动将焦点转到相关的表单控件(input)上. 问题:功能可以使用,但是会有报错. 解决办法:react中label标签没有for属性,用htmlFor代替for属性 demo的代码: 报错 修改后的代码 到此这篇关于react中label标签for报错的文章就介绍到这了,更多相关react中label标签内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

  • React中this丢失的四种解决方法

    发现问题 我们在给一个dom元素绑定方法的时候,例如: <input type="text" ref="myinput" accept = "image/*" onChange = {this.selectFile} /> React组件中不能获取refs的值,页面报错提示:Uncaught TypeError: Cannot read property 'refs' of null or undefind 小栗子 import Re

  • 详解三种方式在React中解决绑定this的作用域问题并传参

    在React中时常会遇到this指向的作用域问题 从而导致undefined报错 先来个Demo: 功能很简单 点击按钮改变文字 import React from 'react'; export default class BindWithThis extends React.Component { constructor(props) { super(props); this.state = { msg:"BindWithThis" } } render() { return &l

  • 关于react中的常见错误及解决

    目录 最近在做react项目的时候遇到了几个报错,这几个报错在react项目还算常见,因此记录下来解决方法. ’type’ is missing in props validation 报错:type缺少props验证 解决: 1.查看下propTypes是否写成大写了,因为我们引入的时候是大写的,所以很多小伙伴可能直接复制过来就成大写了,也会报错哦 2.新增type: PropTypes.number import PropTypes from 'prop-types'; const Repo

  • 在 React 中使用 Redux 解决的问题小结

    目录 在 React 中使用 Redux 解决的问题 在 React 项目中加入 Redux 的好处 React + Redux 安装 Redux React 中 Redux 的工作流程 React 计数器案例 使用 Redux Provide 组件 connect 方法 使用 bindActionCreators 方法继续简化 代码重构 为 Action 传递参数 Redux 弹出框 初始化静态内容 添加默认隐藏状态 定义操作按钮 衍生的问题 拆分合并 reducer 拆分 合并 调整组件 在

  • 解决React报错Rendered more hooks than during the previous render

    目录 总览 顶层调用 条件之上 总览 当我们有条件地调用一个钩子或在所有钩子运行之前提前返回时,会产生"Rendered more hooks than during the previous render"错误.为了解决该错误,将所有的钩子移到函数组件的顶层,以及不要在条件中使用钩子. 这里有个示例用来展示错误是如何发生的. // App.js import {useEffect, useState} from 'react'; export default function App

  • React中的render何时执行过程

    我们都知道Render在组件实例化和存在期时都会被执行.实例化在componentWillMount执行完成后就会被执行,这个没什么好说的.在这里我们主要分析存在期组件更新时的执行. 存在期的方法包含: - componentWillReceiveProps - shouldComponentUpdate - componentWillUpdate - render - componentDidUpdate 这些方法会在组件的状态或者属性发生发生变化时被执行,如果我们使用了Redux,那么就只有

  • 谈谈React中的Render Props模式

    概述 Render Props模式是一种非常灵活复用性非常高的模式,它可以把特定行为或功能封装成一个组件,提供给其他组件使用让其他组件拥有这样的能力,接下来我们一步一步来看React组件中如何实现这样的功能. 简要介绍:分离UI与业务的方法一直在演进,从早期的mixins,到HOC,再到Render Prop,本文主要对比HOC,谈谈Render Props 1 . 早期的mixins 早期复用业务通过mixins来实现,比如组件A和组件B中,有一些公用函数,通过mixins剥离这些公用部分,并

随机推荐