3

I am using react-async along with node.js. React-async is used to use react.js in the asynchronous way. To make ajax calls, I am using super-agent. The PostList Component is the topmost parent component. Through its setTopmostParentState method, the component's state can be changed. I want to call this method from Comp4. The parameter to this method will come from an ajax call using the 'super-agent' node middleware.

How can I change the state from Comp4 ? If it was a parent component and then a child component issue, then the state-changing method could be easily passed as a prop to the child component. But a few nestings make it difficult to pass the parameter from the deeply nested child to the topmost parent.

Code Snippet:

    var Comp4 = React.createClass({

      clickHandler: function() {

        request.get('/api/prods_find/' + cat_id + '/' + subcat_id,

          function(resp) {

            var prods_menu = resp.body; 

* * * //CALL setTopmostParentState of the PostList component with the value prods_menu***

          });

      },
      render: function() {
        '<div onClick={this.clickHandler}>Target element</div>'

      }

    });

    var Comp3 = React.createClass({

      render: function() {

        < Comp4 > < /Comp4>

      }

    });

    var Comp2 = React.createClass({

      render: function() {

        < Comp3 > < /Comp3>

      }

    });

    var Comp1 = React.createClass({

      render: function() {

        < Comp2 > < /Comp2>

      }

    });

    var PostList = React.createClass({

      mixins: [ReactAsync.Mixin],

      getInitialStateAsync: function(cb) {

        request.get('http://localhost:8000/api/posts', function(response) {

          cb(null, {
            prods_menu: response.body
          });

        });

      },
      setTopmostParentState: function(prods_menu) {

        this.setState({

          prods_menu: prods_menu
        });
      },
      render: function() {

        var prods = this.state.prods_menu.prods;

        var menu = this.state.prods_menu.menu;

        return (

          < Comp1 > < /Comp1>

        );
      }

    });
Istiaque Ahmed
  • 4,977
  • 17
  • 59
  • 117
  • 1
    For this use case [Flux](http://facebook.github.io/flux/) was invented. What you want to do is to trigger an action from your component that notifies other components (parent component) to update its state. – Henrik Andersson Jan 06 '15 at 00:09
  • @limelights, any work-around without flux out there as the node-react project is in the middle state? – Istiaque Ahmed Jan 06 '15 at 00:13
  • 2
    The only other way would be to pass a reference to a function from the topmost parent down to the deepest children that when used changes state in the parent. – Henrik Andersson Jan 06 '15 at 00:14
  • @limelights, is this what I mentioned in OP in the case of a parent and a child( 2 components only) ? I also mentioned the problem to use that way in this case ? Can you make an answer ? – Istiaque Ahmed Jan 06 '15 at 00:18
  • @limelights, is what I said correct about passing a function reference down ? – Istiaque Ahmed Jan 06 '15 at 09:53
  • The deeply nested case is the same as the parent/child case -- you just keep passing that function down as a prop to the child until you get to the nested component that needs to call it. It's very verbose and can get tedious but that's the right way to do it without using something like Flux. – Randy Morris Jan 06 '15 at 14:00
  • @RandyMorris, yes could solve in that way. – Istiaque Ahmed Jan 06 '15 at 14:57
  • @RandyMorris , can anyone make an answer so that I can accept that? – Istiaque Ahmed Jan 06 '15 at 19:08

2 Answers2

1

In your specific case, I would suggest passing down a function to be called by your deepest child Component.

var Comp4 = React.createClass({

      clickHandler: function() {

        request.get('/api/prods_find/' + cat_id + '/' + subcat_id,

          function(resp) {

            var prods_menu = resp.body;
            this.props.updateParentState(prods_menu);

          });

      },
      render: function() {
        '<div onClick={this.clickHandler}>Target element</div>'

      }

    });

    var Comp3 = React.createClass({

      render: function() {

        < Comp4 {...this.props} > < /Comp4>

      }

    });

    var Comp2 = React.createClass({

      render: function() {

        < Comp3 {...this.props} > < /Comp3>

      }

    });

    var Comp1 = React.createClass({

      render: function() {

        < Comp2 {...this.props} > < /Comp2>

      }

    });

    var PostList = React.createClass({

      mixins: [ReactAsync.Mixin],

      getInitialStateAsync: function(cb) {

        request.get('http://localhost:8000/api/posts', function(response) {

          cb(null, {
            prods_menu: response.body
          });

        });

      },
      setTopmostParentState: function(prods_menu) {

        this.setState({

          prods_menu: prods_menu
        });
      },
      render: function() {

        var prods = this.state.prods_menu.prods;

        var menu = this.state.prods_menu.menu;

        return (

          < Comp1 updateParentState={this.setTopmostParentState} > < /Comp1>

        );
      }

    });

But as you can see, this is quite cumbersome. That's why Flux was created. Using Flux, you could Dispatch a message to a Store containing the new data, and the Store would notify your Component, re-rendering it.

You can learn more about Flux here https://facebook.github.io/flux/

Breno Ferreira
  • 1,166
  • 9
  • 8
0

Not saying it is perfect, but in situation like this I am using PubSub library for component communication. For example https://www.npmjs.com/package/pubsub-js.

In your topmost component you can do this:

var EVENT_NOTIFY_TOPMOST = "EVENT_NOTIFY_TOPMOST";
componentDidMount: function() {
    eventToUnsubscribe = PubSub.subscribe(EVENT_NOTIFY_TOPMOST, function( msg, data ){
        console.log( data ); //logs "world"
    });
}

componentDidUnmount: function() {
    //don't forget this
    PubSub.unsubscribe(eventToUnsubscribe )
}

And in your deepest child you can do this:

PubSub.publish(EVENT_NOTIFY_TOPMOST , "world");
SM79
  • 1,115
  • 1
  • 13
  • 30