随时反应重新渲染任何东西

时间:2019-08-02 09:21:49

标签: javascript reactjs render

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

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

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

下载后,我会以状态存储json数据,并通过props将其传递到组件中。 该组件处理json对象(单词)并创建很多stateless components单词,并带有一些道具(例如,工具提示,key道具,id,className ... ),然后将它们全部放入数组中,然后返回到转录框。

我注意到的是,每次我编辑父元素的主要状态(而不是转录组件使用的数据)时,单词都会重新呈现(我在chrome dev的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向我显示了浅蓝色边框,该边框反应了重新渲染了这些字。

Example

1 个答案:

答案 0 :(得分: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 :)
}