Search code examples
javascriptreactjsrefluxjs

React/Reflux data flow: One component twice in DOM


Suppose I have the following Tree:

<LandingPage>
    <PageHeader>
        <Menu>
            <ShoppingCart>
        </Menu>
    </PageHeader>
    <MainPage>
        <ShoppingCart>
    </MainPage>
</LandingPage>

The component we care about is the ShoppingCart.

Upon mounting it (componentDidMount) ShoppingCart triggers an action, so that the ShoppingCartStore makes a request to a server and returns a list of articles - triggering a rerender of ShoppingCart .

The way it is set up now, there will always be two requests and two rerenders, because both components are in the dom.

One solution would be to have a common root trigger these requests, but that would be the LandingPage - and one would have to pass the data through PageHeader and Menu and MainPage.

Is there a better solution? Is that good enough?


Solution

  • I use an api.store for all data requests. I call the api.store in the entry app.js. Then I use an action that the api.store listens to for the initial data requests. app.js

    'use strict';
    
    import React  from 'react';
    import ReactDom  from 'react-dom';
    
    import AppCtrl from './components/app.ctrl.js';
    import Actions from './actions/api.Actions';
    import ApiStore from './stores/Api.Store';
    
    window.ReactDom = ReactDom;
    
    Actions.apiInit();
    
    ReactDom.render( <AppCtrl />, document.getElementById('react') );
    api.store

    import Reflux from 'reflux';
    
    import Actions from '../actions/api.Actions';
    import ApiFct from '../utils/sa.api';
    
    let ApiStoreObject = {
      newData: {
        "React version": "0.14",
        "Project": "ReFluxSuperAgent",
        "currentDateTime": new Date().toLocaleString()
      },
      listenables: Actions,
      apiInit() { ApiFct.setData(this.newData); },
      apiInitDone() { ApiFct.getData(); },
      apiSetData(data) { ApiFct.setData(data); }
    }
    const ApiStore = Reflux.createStore(ApiStoreObject);
    export default ApiStore;

    In the component that connects to a store the initial state calls the store data so if the data is already there you get it.

    import React from 'react';
    
    import BasicStore from '../stores/Basic.Store';
    
    let AppCtrlSty = {
      height: '100%',
      padding: '0 10px 0 0'
    }
    
    const getState = () => {
      return {
        Data1: BasicStore.getData1(),
        Data2: BasicStore.getData2(),
        Data3: BasicStore.getData3()
      };
    };
    
    class AppCtrlRender extends React.Component {
       render() {
        let data1 = JSON.stringify(this.state.Data1, null, 2);
        let data2 = JSON.stringify(this.state.Data2, null, 2);
        let data3 = JSON.stringify(this.state.Data3, null, 2);
        return (
          <div id='AppCtrlSty' style={AppCtrlSty}>
            React 0.14 ReFlux with SuperAgent<br/><br/>
            Data1: {data1}<br/><br/>
            Data2: {data2}<br/><br/>
            Data3: {data3}<br/><br/>
          </div>
        );
      }
    }
    
    export default class AppCtrl extends AppCtrlRender {
      constructor() {
        super();
        this.state = getState();
      }
    
      componentDidMount = () => { this.unsubscribe = BasicStore.listen(this.storeDidChange); };
      componentWillUnmount = () => { this.unsubscribe(); };
      storeDidChange = (id) => {
        switch (id) {
          case 'data1': this.setState({Data1: BasicStore.getData1()}); break;
          case 'data2': this.setState({Data2: BasicStore.getData2()}); break;
          case 'data3': this.setState({Data3: BasicStore.getData3()}); break;
          default: this.setState(getState());
        }
      };
    }

    From https://github.com/calitek/ReactPatterns React.14/ReFluxSuperAgent.