Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing React state from outside

I want to make an app using React.js. I want it to be easily customizable from the outside world (e. g. by writing userscripts). The idea I attempted to use is to make some special properties in the root element state (like sidebarItems or playlistCreatedHooks) so addon developers could add something there. My questions are: is this a good way to do so, is there the Right Way™ to achieve something similar to my goal and, finally, how will addon developers access these props?

like image 489
Ale Avatar asked Sep 26 '14 13:09

Ale


People also ask

How can you access the state from outside a component in React?

To pass the state into another component, you can pass it as a prop. Then, inside <ExampleComponent /> , you can access the data as this. props.

How do you access state in React?

The state and props in React are always in an object format. This means that the value could be accessed from the state and props via key-value pair. To access the normal state object, you can use the key name from the object.

How do I access Redux store outside a React component?

You just need to export the store from the module where it created with createStore() . Also, it shouldn't pollute the global window object.

Can I access state of a child component React?

In React we can access the child's state using Refs. we will assign a Refs for the child component in the parent component. then using Refs we can access the child's state. Creating Refs Refs are created using React.


2 Answers

One option is observables. Basically it's an object you can listen to changes on, and create a change on. You could also emit other events like an 'add' event on data.playlists to create the api you want to provide.

// data.js
var data = {
  sidebarItems: Observable([]),
  playlists: Observable([])
};

// app.js
var App = React.createComponent({
  mixins: [data.sidebarItems.mixin("sidebar")],
  render: function(){
    return this.state.sidebar.map(renderSidebarItem);
  }
});

/// userscript.js

// causes the view to update
data.sidebarItems.set(somethingElse); 

// run when someone does data.playlists.set(...)
data.playlists.on('change', function(playlists){

});

// an event you could choose to emit with data.playlists.emit('add', newPlaylist)
data.playlists.on('add', function(newPlaylist){

});

Here's an example (untested) implementation of Observable used above, with an extra function for generating the react component mixin.

var events = require('events'); // or some other way of getting it
var Observable = function(initialValue){
  var self = new events.EventEmitter();
  var value = initialValue;

  self.get = function(){ return value };
  self.set = function(updated){
    value = updated;
    self.emit('change', updated);
  };
  self.mixin = function(key){
    var cbName = Math.random().toString();
    var mixin = {
      getInitialState: function(){ var o = {}; o[key] = value; return o },
      componentDidMount: function(){
        self.on('change', this[cbName]);
      },
      componentWillUnmount: function(){
        self.removeListener('change', this[cbName]);
      }
    }
    mixin[cbName] = function(){
      var o = {}; o[key] = value; this.setState(o);
    };
    return mixin;
  }

  return self;
}
like image 111
Brigand Avatar answered Sep 25 '22 00:09

Brigand


Here is my solution. Thanks to this Observable the React components' state are automatically updated (with its consequences, like re-render the component) and you can even listen for changes outside react thanks to the .on method.

var eventEmitter = {
    _JQInit: function() {
        this._JQ = jQuery(this);
    },
    emit: function(evt, data) {
        !this._JQ && this._JQInit();
        this._JQ.trigger(evt, data);
    },
    once: function(evt, handler) {
        !this._JQ && this._JQInit();
        this._JQ.one(evt, handler);
    },
    on: function(evt, handler) {
        !this._JQ && this._JQInit();
        this._JQ.bind(evt, handler);
    },
    off: function(evt, handler) {
        !this._JQ && this._JQInit();
        this._JQ.unbind(evt, handler);
    }
};

var Observable = function(initialValue, name) {
    var self = eventEmitter;
    var name = name;
    var obj = {
        value: initialValue,
        ops: self
    };

    self.get = function() {
        return obj.value
    };

    self.set = function(updated){
        if(obj.value == updated)
            return;

        obj.value = updated;
        self.emit('change', updated);
    };

    self.mixin = function() {
        var mixin = {
            getInitialState: function() {
                var obj_ret = {};
                obj_ret[name] = obj;

                return obj_ret;
            },
            componentDidMount : function() {
                self.on('change', function() {
                    var obj_new = {};
                    obj_new[name] = obj;

                    this.setState(obj_new);
                }.bind(this));
            }
        };

        return mixin;
    };

    return self;
};

example (using it for showing an alert on another component): //Observable init alert_msg = Observable('', 'alertmsg');

var ConfirmBtn = React.createClass({
    mixins: [alert_msg.mixin()],
    handleConfirm: function(e) {
        e.preventDefault();

        this.state.alertmsg.ops.set(null);

        if(! $('#cgv').is(':checked')) {
            this.state.alertmsg.ops.set('Please accept our terms of conditions');
            return;
        }
    }
}

var AlertPayment = React.createClass({
    mixins: [alert_msg.mixin()],

    render: function() {
        var style = (this.state.alertmsg === null) ? {display: 'none'} : {display: 'block'};

        return (
            <div style={style} className="alertAcceptTerms">
                {this.state.alertmsg.value}
            </div>
        );
    }
});

Hope it helps

like image 30
Bertuz Avatar answered Sep 25 '22 00:09

Bertuz