React application slow performance - reactjs

I am trying to make a application in react for rendering a tree like structure which can be expanded and collapsed on user input, although i have managed to get the app working as I want but the performance is quite slow. I am not sure if this is because of the nature of the application, the react component or my ignorance of the framework.
I have done a chrome profiling and here are the screenshots:
Please if you can help me understand through this images what is the bottleneck and if/how it can be solved.
Source :
https://github.com/harsh-a1/react-skeleton/tree/tree
Component :
export function TreeComponent(props){
var instance = Object.create(React.Component.prototype)
var state = {
previousSelected :{},
onSelectCallback : props.onSelectCallback
}
instance.props = props;
var toggle = function(){
instance.setState(state.data)
}
instance.updateState = function(){
instance.setState(Object.assign({},state))
}
if (!props.data){
init(function(ous){
state.data = ous;
instance.setState(state)
});
}
instance.render = function(){
if (!state.data){return <div key = "dummy"></div>}
return <ul key={"ul_"+state.data.id}>
<Tree data={state.data} updateState={instance.updateState} state={state } />
</ul>
}
return instance;
function Tree(props){
var instance = Object.create(React.PureComponent.prototype)
instance.render = function(){
if (!props.data.children || props.data.children.length == 0){
return (
<li key={"li_"+props.data.id}>
<LeafNode data={props.data} updateState = {props.updateState} state={props.state} />
</li>
)
}
return (
<li key={"li_"+props.data.id}><LeafNode data={props.data} updateState = {props.updateState} state={props.state} />
<ul key = {"ul_"+props.data.id} style={props.data.showChildren?{"display":"inline"}:{"display":"none"}}>
{
props.data.children.map(function(child){
return <Tree data={child} key={"tree_"+child.id} updateState = {props.updateState} state={props.state} />
})
}
</ul></li>
)
}
return instance;
function LeafNode(props){
var instance = Object.create(React.PureComponent.prototype)
instance.props = props;
/* instance.shouldComponentUpdate = function(nextProps) {
return (nextProps.data.showChildren !== this.props.data.showChildren);
}
*/
instance.componentDidMount= function(){
console.log("yes")
}
instance.toggle = function(){
props.data.showChildren = !props.data.showChildren;
props.updateState();
}
instance.selected = function(){
props.state.previousSelected.selected = false;
props.data.selected = !props.data.selected;
props.state.previousSelected = props.data;
props.updateState();
props.state.onSelectCallback(Object.assign({},props.data));
}
instance.render = function(){
var toggleImg = "";
if ( props.data.children.length!=0){
toggleImg = props.data.showChildren ?expandIMG:collapseIMG;
}
return (
<div key={"div_"+props.data.id} >
<span key={"span_"+props.data.id} className="toggle" >
<img key={"img_"+props.data.id} width="12" height="12" src={toggleImg} onClick={instance.toggle} />
</span>
<a key={"a_"+props.data.id} onClick = {instance.selected} style={props.data.selected? {color:"yellow"}:{color:"black"}} >{props.data.name}</a>
</div>
)
}
return instance
}
}
}
Thanks
harsh

Have a look at best practices how to create components and component lifecycle at React website. It is a good idea to follow them so it would be easier to identify problems later.
It is also worth looking at react-virtualized components. There are a bunch of components that could be reused including list, grid, tree etc. Also look at their implementation since it is opensource.
Their virtual list component resolved my issue with rendering 500+ items.

Here is an example with 1M+ nodes and good performance. The trick is to use local state and not render the hidden elements.
https://codesandbox.io/s/z6jr6zww4l

Turns out The issue was that I was using the "development" build to check it....i switched to a production library and now it is running not to bad...still not as good as direct DOM but pretty close...although don't know how much it can scale...

Related

call function in React functional component

I am trying to call a function from a div like the following
<div id='div_abstract'>
{content.abstract && content.abstract.length ? (
<article id="abstract" onMouseUp={spanSelect}>{content.abstract </article>) : ''}
</div>
My functional component is structured like this
export default function ExternalInfos(props) {
...
function spanSelect() { ... }
return(
...
);
}
And the function I'm trying to call is
let table = [];
function spanSelect() {
var span = document.createElement("span");
span.setAttribute("id","span");
if (window.getSelection()) {
var text = window.getSelection();
if (text.rangeCount) {
var range = text.getRangeAt(0).cloneRange();
range.surroundContents(span);
text.removeAllRanges();
text.addRange(range);
};
};
let object = window.getSelection().toString();
table.push(object);
const annotation = document.getElementById("annotationArea");
annotation.updateObjectAnnotation(table);
}
But nothing happens when I select text from my div and it doesn't return an error.
How do I solve this?
You need to capitalize the event handler prop: onMouseUp.
From the React docs (https://reactjs.org/docs/handling-events.html):
"React events are named using camelCase, rather than lowercase."

Nested drag and drop with react dragula

I am working on a react project and I want to drag tasks to their subtasks with react Dragula like nestable.
Code:
dragulaDecorator = (componentBackingInstance) => {
if (componentBackingInstance) {
this.containers.push(componentBackingInstance)
}
};
componentDidMount() {
this.isMount = true;
let tasks = this.props.tasks
this.setState({ allTasks: tasks})
this.drake = dragula(this.containers, {
isContainer: function (el) {
return el.id === 'single-task';
},
moves: function (el, source, handle, sibling) {
return handle.classList.contains('icon-handle');
}
});
}
<ul className="tjs-list list-unstyled single-task" ref={this.dragulaDecorator}>
{this.state.allTasks && (this.state.allTasks.length > 0) && this.state.allTasks.map((field, key) => {
return (
<li key={key}>
<h4>
<i className="tjsicon-sort icon-handle"></i>
{field.name}
</h4>
</li>
)
})}
</ul>
I created simple drag and drop with the above-mentioned code but I need to drag tasks in subtasks and their subtasks so on. So please suggest to me how I can achieve what I want
Go for native library https://github.com/bevacqua/dragula
You can use class "nested" for the nested elements.
className="nested"
And find ".nested" using the query selector.
[].slice.apply(document.querySelectorAll(".nested"))
This is a working demo
https://codesandbox.io/s/spring-violet-ozlxh

ReactJS - embedding custom element in a custom element

I just started to learn ReactJS and encountered a problem that I can't solve.
I'm creating a basic application for TV Shows. I have a bootstrap tab for every season of a show and within this tab I want to list all the episodes of the selected season. The problem is that I have to create the tabs in a loop, and within this loop I should have another loop for the episodes.
I'm trying to do something like this:
EpisodeCards(props) {
return (
<div>
This should contain the details of the episodes
</div>
)
}
SeasonTabs(props) {
console.log('Seasons: ', props.seasons)
let tabs = [];
let episodes = [];
let currentSeason = 1;
let id;
let aria;
for(let season of props.seasons) {
episodes = [];
id="nav-season" + currentSeason;
aria = "nav-season" + currentSeason + "-tab";
tabs.push(<div className="tab-pane fade" id={id} role="tabpanel" aria-labelledby={aria}><this.EpisodeCards episodes={season}></this.EpisodeCards></div>);
currentSeason++;
}
return (
<div className="tab-content py-3 px-3 px-sm-0" id="nav-tabContent">
{tabs}
</div>
)
}
For this I am getting the following error:
Unhandled Rejection (TypeError): Cannot read property 'EpisodeCards' of undefined
How can this be done in the 'react way'? Thanks in advance.
Change
SeasonTabs(props)
to
SeasonTabs = (props) =>
You want to access a class property using this but by default its not binded to the function (ES5 only) by creating the functions using arrow () =>(new ES6 syntax) it automatically bind this to the function.
For Example:
class Test extends Component{
constructor(props){
this.testFn= this.testFn.bind(this);
}
testFn(){
console.log(this); //will output
}
testFn2(){
console.log(this); // undefined
}
testFn3 = () =>{
console.log(this); //will output
}
}
Reactjs is all about components, everything is component. If a function return a react element or Custom element then is a component.
You are creating a functional component inside of a class component, while this approach may work but this is not appropriate way to make component.
It is better to use composition, your code will be more clear, easier
to read, better maintainability and you can use component in other
components.
My solution:
function EpisodeCards(props) {
return (
<div>
This should contain the details of the episodes
</div>
)
}
SeasonTabs(props) {
console.log('Seasons: ', props.seasons)
let tabs = [];
let episodes = [];
let currentSeason = 1;
let id;
let aria;
for(let season of props.seasons) {
episodes = [];
id="nav-season" + currentSeason;
aria = "nav-season" + currentSeason + "-tab";
//There is no need for this keyword
tabs.push(<div className="tab-pane fade" id={id} role="tabpanel" aria-labelledby={aria}><EpisodeCards episodes={season}/></div>);
currentSeason++;
}
return (
<div className="tab-content py-3 px-3 px-sm-0" id="nav-tabContent">
{tabs}
</div>
)
}

React - Not able to update list of product component

I made a list of product which contains name, price etc.. properties. Then I created a simple search box and I am searching my products based on product name . the search result returns the correct object but the UI is not getting updated with that result.Initially, I am able to see the list but after searching it is not getting updated. I am new to react SO need some help. here is my code
OnInputChange(term)
{
let result= this.products.filter(product=>{
return product.name==term;
});
console.log(result);
let list=result.map((product)=>
{
return <li key={product.price}>{product.name}</li>
});
console.log(list);
this.setState({listOfProducts:list});
}
render()
{
this.state.listOfProducts=this.products.map((product)=>
{
return <li key={product.price}>{product.name}</li>
});
return <div>
<input onChange={event=>{this.OnInputChange(event.target.value)}}/>
<ul>{this.state.listOfProducts}</ul>
</div>
}
}`
this.products.filter should probably be this.state.products.filter
When referring to a Components method you say this.onInputChange but when referencing state you must say this.state.products because this.products doesn't exist.
I would also move this:
let list=result.map((product)=>
{
return <li key={product.price}>{product.name}</li>
});
to your render statement. so your onchange handler could be:
OnInputChange(term)
{
let result= this.products.filter(product=>{
return product.name==term;
});
this.setState({listOfProducts:result});
}
and then you
render(){
return(
{this.state.listOfProducts.map(product => {
return <li key={product.price}>{product.name}</li>
})}
)
}
hope that helps! If your problem persists please share your entire code so I can better understand the issue.

How to "override" the render function?

I'm just getting started with React. I have a project that includes many tables, some pretty complex, but all generally output a table from a set of data. Some have parent-child relationships (with toggleable child rows), and each has a slightly different format for the row output (e.g. some have buttons that open modals). Normally, I would use jQuery DataTables for this, which is easy and flexible for something like this. I'm struggling to figure out how to do it sustainably (scaleably?) in React, though.
I've written a basic table component where it accepts a set of items via props and spits out a table and handles child rows via internal state. Today I'll convert that to use two components: the table and a separate one for the rows. I really don't want to have to write X or 2X different components though (where X is the number of tables in the project), but I'm not sure how to make it reusable. All of the tables should have some things in common like style, filter capability, paging, etc., which I would try to put at the Table component level and reuse.
The best solution so far that I've thought about doing is passing in a preRender function via props and using that to create the actual row JSX, and having the render function just assemble all of those snippets into one output (which is basically what I do already but with Array.map. I could then provide the preRender via a prop (if that works), like this:
var Table = React.createClass({
render: function() { // mixin?
var rows = [];
for (var i=0; i<this.props.items.length; i++) {
rows.push(this.props.preRender(this.props.items[i]));
}
return rows; // plus html boilerplate...
}
});
var Parent = React.createClass({
render: function() {
var foodItems = this.state.foodItems;
var drinkItems = this.state.drinkItems;
var foodRender = function(i) { return (<tr>{i} <a href?>Buy me</a></tr>); }
var drinkRender = function(i) { return (<tr>{i} <button>Drink me</button></tr>); }
return (
<Table items={foodItems} preRender={foodRender}/>
<Table items={drinkItems} preRender={drinkRender}/>
);
}
});
Another thing I thought of was somehow passing in a different Row component to the Table component, if that's possible. I guess the root problems are:
The table-level stuff is very similar or identical across tables but may have parts needing customization per-table.
The rows do things like open popovers, so they will have a state (or need to circle around w/props).
Is there a better way to do this sort of logic/dependency injection, or will I probably just need to make a ton of slightly-different controls.
I'd start with something like this (using ES6 syntax for brevity):
const FoodItem = React.createClass({
render() {
return (
<tr><td>{this.props.item} Buy me</td></tr>
);
}
});
const DrinkItem = React.createClass({
render() {
return (
<tr><td>{this.props.item} <button>Drink me</button></td></tr>
);
}
});
const Table = React.createClass({
render() {
const {items, itemComponent: ItemComponent} = this.props;
return (
<table>
{items.map(item => <ItemComponent item={item} />)}
</table>
);
}
});
const Parent = React.createClass({
render() {
return (
<div>
<Table items={this.state.foodItems} itemComponent={FoodItem}/>
<Table items={this.state.drinkItems} itemComponent={DrinkItem}/>
</div>
);
}
});
An alternative pattern would be this (which is best depends on your requirements):
const FoodItem = React.createClass({
render() {
return (
<tr><td>{this.props.item} Buy me</td></tr>
);
}
});
const DrinkItem = React.createClass({
render() {
return (
<tr><td>{this.props.item} <button>Drink me</button></td></tr>
);
}
});
const Table = React.createClass({
render() {
// I'm assuming you ultimately want your table to do something cleverer than just rendering a table element
return (
<table>{this.props.children}</table>
);
}
});
const Parent = React.createClass({
render() {
return (
<div>
<Table>{this.state.foodItems.map(item => <FoodItem item={item} />)}</Table>
<Table>{this.state.drinkItems.map(item => <DrinkItem item={item} />)}</Table>
</div>
);
}
});

Resources