【问题标题】:React re-render anything anytimeReact 随时重新渲染任何东西
【发布时间】:2019-08-07 22:45:28
【问题描述】:

我是 React 新手,我正在构建一个 Web 应用程序,它显示一个视频和一个带有转录词的框。

单词可以编辑,并且可以通过工具提示或对话框显示一些数据。

主要的 React 组件从 API 下载数据并组成由一些组件组成的页面:顶栏、侧边栏、播放器和转录框。

下载后,我将 json 数据存储在 state 中,并通过 props 将其传递给组件。 该组件处理 json 对象(单词)并创建很多 stateless components,单词,还有一些道具(例如,工具提示,key 道具,id,className ...)然后我把所有这些都在一个数组中并返回到转录框。

我注意到,每次我编辑父元素的主要状态(不是转录组件中使用的数据)时,单词都会重新渲染(我在 chrome 开发工具的 react 选项卡中启用了highlighy updates) .

这对于少量单词(例如 50-100 个元素)没有问题,但对于数千个项目,它会在用户操作和图形更新之间产生很大的延迟。

我可能错过了重点:我做错了什么?

您能告诉我如何处理大量 JSON 对象项以正确方式响应组件吗?


编辑:我切断了不感兴趣的代码,留下最少的代码来重现我的意思。

此示例中包含转录和其他组件的主要组件是 Info.js。我把API下载的数据放在一个单独的文件中,因为数据很长,你可以在那里找到:https://pastebin.com/nw1A391y

Info.js

import React, { Component } from 'react';
import TranscriptionBox from '../partials/TranscriptionBox.js';

class Info extends Component {

    /**
     * Set default props
     * @type {Object}
     */
    static defaultProps = {
        name: 'Info'
    }

    /**
    * Component constructor
    * @param {Object} props [Component props]
    */
    constructor (props) {
        // Make property available in this module
        super(props);

        this.state = {
            task: {
                attachments: [],
                automatic_created: true,
                clips: [],
                creation_user: "https://example.com/",
                end_datetime: "2019-08-02T04:30:03.022",
                id: 498,
                metadata: [],
                owner_user: null,
                source: "https://example.com/",
                source_repr: "Hello",
                start_datetime: "2019-08-02T04:00:03",
                status: "https://example.com/",
                status_repr: "created",
                url: "https://example.com/"
            },
            transcription: [] // NOTE: Put here the transcription form the file attached!
        }
    }

    onWordClick = () => {
        console.log("onWordClick");
        this.setState({
            hello: 'onWordClick'
        })
    }

    onActionChange = () => {
        console.log("action change");
        this.setState({
            hello: 'onActionChange'
        })
    }

    onTaskProgressChange = () => {
        console.log("onTaskProgressChange");
        this.setState({
            hello: 'onTaskProgressChange'
        })
    }

    render() {
        return (<div>
            <TranscriptionBox
                key="transcription_box_component"
                ref="transcription_box_component"
                task={this.state.task}
                transcription={this.state.transcription}
                taskLoaded={true}
                action={null}
                progress={null}
                selected={null}
                isSpecialPressed={false}
                handleActionChange={this.onActionChange}
                handleWordClick={this.onWordClick}
                handleProgressChange={this.onTaskProgressChange}
                />
        </div>);
    }
}

export default Info;

这是转录框组件,用于渲染 API 数据并在滚动框内生成文字对象。

TranscriptionBox.js

import React from 'react';
// Import classes and components
import Word from './Word.js';
// Import style
import '../styles/TranscriptionBox.css'

class TranscriptionBox extends React.Component {

    /**
    * Set default props
    * @type {Object}
    */
    static defaultProps = {
        name: 'TranscriptionBox',
        tempData: undefined
    }

    /**
    * Component constructor
    * @param {Object} props [Component props]
    */
    constructor (props) {
        // Make property available in this module
        super(props);
        // Set default state
        this.state = {
            visible: true,
            value: '',
            transcription: this.props.transcription,
            lastPostion: 0,
            inDownloading: false,
            downloaded: false
        }
    }

    /*
    |-------------------------------------------------------------------------|
    |                               ACTION HANDLE                             |
    |-------------------------------------------------------------------------|
    */

    /**
     * Handle click action on transcription box word
     * @param  Event ev     Double click event
     * @return void
     */
    handleWordClick = (ev) => {
        // Get current action
        let action = this.props.action;
        // Get the target of click
        let target = ev.currentTarget;
        // Init word object
        let data = false;
        // Manage wordclick
        this.props.handleActionChange(undefined);
    }

    /*
    |-------------------------------------------------------------------------|
    |                            ELEMENTS GENERATION                          |
    |-------------------------------------------------------------------------|
    */

    /**
     * Call the ReactJS dangerouslySetInnerHTML to transform string into HTML
     * @return {HTML}   Html code
     */
    renderTranscription = () => {
        console.log("*** TB@renderTranscription");
        // Create HTML tag from text transcription
        return this._elaborateTranscription(this.props.transcription);
    }

    /**
     * Elaborate transcription and create all tags
     * // COMBAK: make clips tag manage array of clips
     * @param  array transcription  The transcription to elaborate
     * @return array                The transcription elaborated
     */
    _elaborateTranscription = (transcription) => {
        // Init vars
        let reactWord, word, wordObject, seconds;
        // Init array
        let elaborated = [];

        // If transcription is null or empty transcription
        if (!transcription || transcription.length < 1) {
            // Init reactWord to be pushed
            reactWord = <em key="no-transcription">Loading...</em>;
            // Return a placeholder
            elaborated.push(reactWord);
            // Return elaborated
            return elaborated;
        }

        // Get this context to me var
        let me = this;

        // Iterate each word and take the iterator count
        transcription.forEach(function (transcriptionWord, i) {
            // Create a copy
            wordObject = {...transcriptionWord};

            // If has no id
            if (!transcriptionWord['id']) {
                // Compose an unique id
                wordObject['id'] = "word-" + i;
            }

            // Get seconds
            seconds = parseInt(transcriptionWord['time']);
            // Calculate the seconds at
            wordObject['seconds'] = seconds;


            // Create a new word react element
            word = <Word key={wordObject.id}
                    handleDoubleClick={me.handleWordDoubleClick}
                    handleClick={me.handleWordClick}
                    {...wordObject} />;
            // Push created tag into array
            elaborated.push(word);
        });

        // Return elaborated text
        return elaborated;
    }

    /*
    |-------------------------------------------------------------------------|
    |                                   RENDER                                |
    |-------------------------------------------------------------------------|
    */

    /**
    * Render component
    * @return {} []
    */
    render() {
        return (
            <div
                id="transcription-box"
                ref="transcription-box"
                className='task-transcription-box-container border-secondary'>
                {this.renderTranscription()}
            </div>
        );
    }
}

// Export default component to be accessible in other components
export default TranscriptionBox;

这是无状态组件这个词。

Word.js

import React from 'react';

import { TooltipHost } from 'office-ui-fabric-react/lib/Tooltip';
import { Icon } from 'office-ui-fabric-react/lib/Icon';

const Word  = props => {
    return <div
        key={props.id}
        className='transcription-word'
        data-id={props.id}
        data-metadata=''
        data-clips=''
        data-seconds=''
        data-datetime=''
        onDoubleClick={props.handleDoubleClick}
        onClick={props.handleClick}
        >
            {props.data}&nbsp;
        </div>;
};

export default Word;

我为这个问题制作了一个简短的视频。在这个 gif 中你可以看到每次我按下一个词(方法 handleClick 被调用)和一个无用的 Info.js 状态变化的道具。没有触及任何文字数据,但 chrome 显示带有浅蓝色边框的 react 已经重新渲染了文字。

【问题讨论】:

  • 好的,将编写一部分代码来重现它们。
  • @codekaizer,我用代码和更多问题解释更新了我的问题。谢谢

标签: javascript reactjs render


【解决方案1】:

根据我从您的问题中了解到的情况,您渲染了很多不必要的组件?

您可以为stateless components 做的就是控制它们何时渲染。例如,当他们收到不同的道具时,渲染。 (使它们成为无状态的纯组件)。你可以使用 memo() 。例如:

import React, { memo } from "react";
import { isEqual } from "../definedByYou";

const YourComponent = props => {
  return <div>{props.text}</div>;
};

// When text changes (isEqual returns false) => component will render
function arePropsEqual(prevProps, nextProps) {
  return isEqual(prevProps.text, nextProps.text);
}

export default memo(YourComponent, arePropsEqual);

其中isEqual 是您定义的函数,当比较的道具相同时返回true

编辑

手风琴到你的 Word 组件试试:

import React, { memo } from "react";

import { TooltipHost } from 'office-ui-fabric-react/lib/Tooltip';
import { Icon } from 'office-ui-fabric-react/lib/Icon';

const Word  = props => {
    return <div
        key={props.id}
        className='transcription-word'
        data-id={props.id}
        data-metadata=''
        data-clips=''
        data-seconds=''
        data-datetime=''
        onDoubleClick={props.handleDoubleClick}
        onClick={props.handleClick}
        >
            {props.data}&nbsp;
        </div>;
};

// if your data prop is an object you can't just use "==="
function arePropsEqual(prevProps, nextProps) {
  return prevProps.id === nextProps.id && prevProps.data === nextProps.data;
}

export default memo(Word, arePropsEqual);

编辑 2

在 TranscriptionBox 组件中使用 shouldComponentUpdate:

shouldComponentUpdate(nextProps, nextState) {
  return true; // Here check differences in state and props :)
}

【讨论】:

  • 嗨@vicente,感谢您的回复!我根据您的建议编辑我的 word 无状态对象。我错过了React.memo,我注意到性能有所提高,谢谢!然而,动作总是从用户交互延迟几秒钟(从 1 到 4-5)。我将尝试在我的所有组件中使用此方法:您还有其他提高性能的建议吗?
  • 随时!如我错了请纠正我。当handleClick in Word.js -> handleWordClick 在 TranscriptionBox.js -> onActionChange in Info.js -> setState 你好?这个hello 信息会在 TranscriptionBox 中使用吗?
  • 在这个例子中不,在 Web 应用程序中它会改变 Info 组件状态,从而导致图形更改(如打开模式)。
  • 所以当你调用handleClick它不应该重新渲染TranscriptionBox组件,它应该渲染模态。尝试使用 shouldComponentUpdate 控制 TranscriptionBox 的渲染。
  • 感谢您的回复。我尝试在所有类组件中管理shouldComponentUpdate,并在所有功能组件中集成React.memo(),但性能仍然不好。我认为问题出在组件之间传递的数据中:主要数据存储在Info.js 组件状态中。状态数据通过 props 传递给其他子组件(如转录数据到 TranscriptionBox 组件)。您知道是否有更好的方法在组件之间共享数据?如果我全部移到redux,性能可以提高吗? (重构会花费我很多时间)
猜你喜欢
  • 1970-01-01
  • 2019-03-21
  • 2017-10-06
  • 2017-10-08
  • 2021-07-27
  • 2022-11-15
  • 1970-01-01
  • 1970-01-01
  • 2022-11-21
相关资源
最近更新 更多