简体   繁体   中英

How can I have different data in different ReactJS Component instances?

Let's say I have 2 <Logs/> components. They're only displayed when their parent <Block/> has been clicked. Each <Block/> only has one <Logs/> but there will be many <Blocks/> on the page.

class Block extends React.Component {

    toggleExpanded() {

        this.setState({
            isExpanded: !this.state.isExpanded
        });

    }

    render() {

        let blockId = // ...

        return (
            <div>
                <button type="button" onClick={() => this.toggleExpanded()}>Toggle</button>
                {this.state.isExpanded && <Logs blockId={blockId} />}
            </div>
        );

    }

}

export default Block;

As the <Logs/> is created, I want to get data from the server using Redux. There could be a lot of <Logs/> some day so I don't want to load in all data to begin with, only data as needed.

class Logs extends React.Component {

    componentDidMount() {

        if (!this.props.logs) {
            this.props.fetchBlockLogs(this.props.blockId);
        }

    }

    render() {

        return (this.props.logs && this.props.logs.length)
            ? (
                <ul>
                    {this.props.logs.map((log, i) => <li key={i}>{log}</li>)}
                </ul>
            )
            : (
                <p>Loading ...</p>
            );

    }

}

SupportLogs.defaultProps = {
    logs: null
}

const mapStateToProps = (state) => ({
    logs: state.support.logs
});

const mapDispatchToProps = (dispatch, ownProps) => ({
    fetchBlockLogs: (blockId) => {
        dispatch(ActionTypes.fetchBlockLogs(blockId));
    }
});

export default connect(mapStateToProps, mapDispatchToProps)(SupportLogs);

Currently, as I toggle one parent <Block/> closed and then re-opened, the data is remembered. That's a nice feature - the data won't change often and a local cache is a nice way to speed up the page. My problem is that as I toggle open a second <Block/> , the data from the first <Logs/> is shown.

My question is: how can I load in new data for a new instance of <Logs/> ?

Do I have to re-load the data each time and, if so, can I clear the logs property so that I get the loading animation back?

Maybe think about changing the logs in your store to be an object with logs loaded into it keyed by block id:

logs: {
 'someBlockId': [
   'some logline', 
   'some other logline'
 ]
]

Have your reducer add any requested logs to this object by block id, so as you request them they get cached in your store (by the way I really like that your component dispatches an action to trigger data fetching elsewhere as a side effect, rather than having the fetch data performed inside the component :) ).

This reducer assumes that the resulting fetched data is dispatched in an action to say it has received the logs for a particular block, {type: 'UPDATE_LOGS', blockId: 'nic cage', receivedLogsForBlock: ['oscar', 'winning', 'actor']}

logsReducer: (prevState, action) => {
  switch(action.type)
    case 'UPDATE_LOGS':
      return Object.assign({}, prevState, {
        [actions.blockId]: action.receivedLogsForBlock
      })
    default: return prevState
  }
}

And in your component you can now look them up by id. If the required block is not defined in the logs object, then you know you need to send the action for loading, and can also show loader in meantime

class Logs extends React.Component {

  componentDidMount() {
    const {logs, blockId} = this.props
    if (!logs[blockId]) {
      this.props.fetchBlockLogs(blockId);
    }
  }

  render() {
    const {logs, blockId} = this.props
    return (logs && logs[blockId]) ? (
        <ul>
          {logs[blockId].map((log, i) => <li key={i}>{log}</li>)}
        </ul>
      ) : (
        <p>Loading ...</p>
      )
  }

}

This has the added bonus of logs[blockId] being undefined meaning not loaded , so it is possible to distinguish between what needs to be loaded and what has already loaded but has empty logs, which your original would have mistaken for requiring a load

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM