我是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}
</div>;
};
export default Word;
我简短地介绍了这个问题。在此gif文件中,您可以看到每次我按下一个单词(调用方法handleClick
)和无用的Info.js
状态更改道具。没有触摸任何字数据,但是chrome向我显示了浅蓝色边框,该边框反应了重新渲染了这些字。
答案 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}
</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 :)
}