2

In my application, I have a state-dependent component that will be updated after an asynchronous event that is not handled inside the component.

var Status = React.createClass({
    getInitialState: function () {
        return {
            state: 'loading'
        };
    },
    render: function () {
        return React.createElement('span', null, this.state.state);
    }
});

var status = React.createElement(Status);
React.render(status, mountNode);

I want to be able to do something like this:

doAsyncThing(function callback() {
    status.setState({ state: 'done' });
});

I understand that the async logic could be moved to componentDidMount, as shown here, but is it possible to move it outside the component? Or is it simply the React zen to place such logic inside the component?

deleted user
  • 824
  • 1
  • 7
  • 11
  • 1
    You can setup a pubsub pattern, that's the other alternative mentioned in the docs. – elclanrs Aug 18 '15 at 03:19
  • possible duplicate of [Update component state from outside React (on server response)](http://stackoverflow.com/questions/31856712/update-component-state-from-outside-react-on-server-response) – Axxiss Aug 19 '15 at 17:27
  • @Axxiss although the question is similar (the other is a little more specific perhaps), the accepted answer in that question, setting a callback to a global variable and calling that callback, is messy and unreusable (cannot have multiple components change state upon event). The pubsub patttern is robust and reusable. – deleted user Aug 19 '15 at 22:17
  • that's true, then maybe you should take a look at the Flux Pattern – François Richard Aug 20 '15 at 00:35

2 Answers2

1

Pubsub pattern may be your solution(in this case use Backbone):

var ps=_.extend({},Backbone.Events);
var Status = React.createClass({
    getInitialState: function () {
        return {
            state: 'loading'
        };
    },
    componentDidMount: function(){
       ps.on('done',function(){
            status.setState({ state: 'done' });
       });//your callback function
    },
    render: function () {
        return React.createElement('span', null, this.state.state);
    }
});

var status = React.createElement(Status);
React.render(status, mountNode);

And then trigger the done event in doAsyncThing callback:

doAsyncThing(function callback() {
    ps.trigger('done');
});
L_K
  • 2,838
  • 2
  • 16
  • 36
  • Same idea as elclanrs, going with pubsub except: changing `status.setState` to `this.setState` for reusability. Also assuming `pb` is supposed to be `ps`. – deleted user Aug 19 '15 at 22:08
  • @keizom Yeah, pubsub can decouple our component. It should be ps :) – L_K Aug 21 '15 at 06:51
  • Let's say I have the async function in another module and I'm importing it into the component module. How can I use setState in that case? I have asked the same question [(check the link here)](https://stackoverflow.com/questions/48911154/how-to-render-value-in-promises-on-button-click-in-react/48911632?noredirect=1#comment84831371_48911632) and I haven't gotten any response. – Uchiha Madara Feb 22 '18 at 18:54
1

I would place it in componentDidMount indeed but you can choose to place your call outside and render your view on success passing the data into props.

Also this is considered as an anti-patter unless this is only for an initial loading.

See this: https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html

Edit: Here is a clearer explanation, same question bind(this) not working on ajax success function

Easiest way to do it use componentDidMoun : https://facebook.github.io/react/tips/initial-ajax.html

Pub/sub will work but I think it's overkill maybe not the best solution. Let me know what you think.

Community
  • 1
  • 1
François Richard
  • 6,817
  • 10
  • 43
  • 78
  • Yes, this is an anti-pattern. And yes, it's acceptable to use props as an initial state. However, you are suggesting "passing the data into props" once the async finishes, which is not the same. – deleted user Aug 19 '15 at 22:27
  • I don't understand what you are saying. It's passing data into props for intitial rendering. That's what is described in the link. You can do it the way you want, after the async finishes or after a walk outside it's up to you. – François Richard Aug 19 '15 at 23:24
  • What data are you passing into the props and what do you do once the async finishes? Code could help you explain clearer. – deleted user Aug 19 '15 at 23:31
  • Well you are passing the data of your callback/event/wtv on rendering mycall.success: function(data) { } something like that – François Richard Aug 19 '15 at 23:39
  • Let's think chronologically: 1) before async finishes, I create a component and pass nothing to `initialdata`. 2) the async finishes, and I recreate the component and pass `data` into `initialdata`. In this case `intialdata` is not initial; its value changes. – deleted user Aug 19 '15 at 23:53
  • no no you render on SUCCESS , when your async is finished. But this is ok if you use it only once. Check this link I edited in my answer I think it may answer your questions: http://stackoverflow.com/questions/31995180/bindthis-not-working-on-ajax-success-function/31996156#31996156 There is some code I was lazy to reproduce here but this is the same thing. Unles you are already using backbone this is wayyyyy overkill to implement it to do that. – François Richard Aug 19 '15 at 23:55
  • I do not want to render on *just* success. The entire purpose of my component is to provide immediate feedback before the async finishes, thus requiring to render twice. Also, backbone is not necessary for an event bus, that is not an issue for me. ----- This question is already answered and I am choosing to go with the pubsub pattern. Thanks for you answer. – deleted user Aug 20 '15 at 00:24
  • Then you should work in componentDidMount it's a very classic way to do it, it's documented clearly by react people themself : https://facebook.github.io/react/tips/initial-ajax.html – François Richard Aug 20 '15 at 00:33