ElJefeJames ElJefeJames - 2 months ago 9
React JSX Question

How do you isolate changes in React Props/State

Within the context of Meteor/React I have a table component that subscribes to a Mongo Database, this subscription has a limit parameter that can be updated via props. I'm not sure why but it appears that the componentDidMount Lifecycle function is firing almost continually leading to unpleasant flickering.

The code is quite lengthy but I've attached what I think is the problematic section - more generally, how do you go about isolating what change in state/props is leading to a re-render? I've tried monitoring Chrome Tools but see no change there.

Any feedback or help appreciated!

import React from 'react';
import booksSingleLine from '../booksTable/booksSingleLine';
import TrackerReact from 'meteor/ultimatejs:tracker-react';


export default class booksListingTable extends TrackerReact(React.Component) {

constructor(props) {
super(props);
console.log("Constructor props are" + this.props.LimitProp);
}

componentWillMount() {
console.log("Mounting booksListingTable");
console.log("Will mount props are" + this.props.LimitProp);
this.state = {
}
}

componentDidMount(props){
// console.log("Did mount and props are" +props.LimitProp);
var limit = this.props.LimitProp
limit = parseInt(limit) || 5;
this.state = {
subscription: {
booksData: Meteor.subscribe("allbooks",{sort: {_id:-1}, limit: limit})
}
}
}

componentWillReceiveProps(props) {
console.log("component will get new props " + props.LimitProp);
// Note that for this lifecycle function we have to reference the props not this.props
// console.log("component will get weird props " + this.props.LimitProp);
var limit = props.LimitProp
limit = parseInt(limit) || 5;
this.state = {
subscription: {
booksData: Meteor.subscribe("allbooks", {limit: limit})
}
}
}

componentWillUnmount() {

}

booksDataLoad(){
var filteredCity = this.props.FilterProp;
console.log("filter is " + filteredCity);
if (filteredCity) {
console.log("just passing a few things")
return (
remotebookss.find({location: filteredCity}).fetch()
)
}
else {
console.log("passing everything to table");
return(
remotebookss.find().fetch()
)}}

render() {
return(
<div>
<table className="ui celled table">
<thead>
<tr>
<th onClick ={this.props.HeaderOnClick}>Name</th>
<th>Date</th>
<th>Summary</th>
<th>Site address</th>
<th>Price is</th>
</tr></thead>
<tbody>
{this.booksDataLoad().map( (booksData)=> {
return <booksSingleLine key={booksData._id} booksData={booksData} />
})}
</tbody>
<tfoot>
<tr><th colspan="3">
<div class="ui right floated pagination menu">
<a class="icon item">
<i class="left chevron icon"></i>
</a>
<a class="item" onClick= {this.props.methodLoadMore}>Load More</a>
<a class="icon item">
<i class="right chevron icon"></i>
</a>
</div>
</th>
</tr></tfoot>
</table>
</div>
)
}
}

Answer

I would strongly recommend you read the following blog post -- https://www.discovermeteor.com/blog/data-loading-react/

Things I see:

  1. You're resetting state all over the place
  2. You're not managing the subscription as a clear object - your code leaks them (FYI).

Here's a version that uses props and default props to setup your Limit, with that it checks only on startup and change to change the subscription.

import React from 'react';
import booksSingleLine from '../booksTable/booksSingleLine';
import TrackerReact from 'meteor/ultimatejs:tracker-react';


export default class booksListingTable extends TrackerReact(React.Component) {
    static propTypes = {
        LimitProp: React.PropTypes.number,
    }

    static defaultProps = {
        LimitProp: 5,
    }

    constructor() {
        super();
        console.log("Constructor props are" + this.props.LimitProp);

        const subscription = Meteor.subscribe("allbooks",{sort: {_id:-1}, limit: this.props.LimitProp})
        this.state = {
            booksData: subscription,
        }
    }

    componentWillReceiveProps(nextProps) {
        console.log("component will get new props " + nextProps.LimitProp);

        // Start new subscription - if it's changed
        if (this.props.LimitProp != nextProps.limitProp) {
            // Stop old subscription
            this.state.booksData.stop()
            // Setup new subscription
            const subscription = Meteor.subscribe("allbooks",{sort: {_id:-1}, limit: nextProps.LimitProp})
            this.setState({ booksData: subscription })
        }
    }

    componentWillUnmount() {
        // Stop old subscription
        this.state.booksData.stop()
    }

    booksDataLoad(){
        var filteredCity = this.props.FilterProp;
        console.log("filter is " + filteredCity);
        if (filteredCity) {
            console.log("just passing a few things")
            return (
              remotebookss.find({location: filteredCity}).fetch()
            )
        }
        else {
            console.log("passing everything to table");
            return(
                remotebookss.find().fetch()
            )
        }
    }

    render() {
        return(
              <div>
                <table className="ui celled table">
                <thead>
                  <tr>
                  <th onClick ={this.props.HeaderOnClick}>Name</th>
                  <th>Date</th>
                  <th>Summary</th>
                  <th>Site address</th>
                  <th>Price is</th>
                </tr></thead>
            <tbody>
            {this.booksDataLoad().map( (booksData)=> {
                    return <booksSingleLine key={booksData._id} booksData={booksData} /> 
                })
            }
                </tbody>
                <tfoot>
                  <tr><th colspan="3">
                    <div class="ui right floated pagination menu">
                      <a class="icon item">
                        <i class="left chevron icon"></i>
                      </a>
                      <a class="item" onClick= {this.props.methodLoadMore}>Load More</a>
                      <a class="icon item">
                        <i class="right chevron icon"></i>
                      </a>
                    </div>
                  </th>
                </tr></tfoot>
              </table>
            </div>
        )
    }
}