Communication between Reactjs Components
Asked Answered
Z

1

1

After struggling too much with Redux, flux and other pub/sub methods i ended up with the following technique. I do not know if that can cause some big damage or flaws so posting it here to get some light from the experienced programmers about its pros and cons.

var thisManager = function(){

    var _Manager = [];
    return{
        getThis : function(key){
            return  _Manager[key];
        },
        setThis : function(obj){            
            _Manager[obj.key] = obj.value;
        }
    }
};
var _thisManager = new thisManager();

// React Component
class Header extends Component{
   constructor(){
      super();
      _thisManager.setThis({ key: "Header", value:this}
   }
    someFunction(data){
        // call this.setState here with new data. 
   }
   render(){
      return <div />
   }
}

// Then from any other component living far somewhere you can pass the data to the render function and it works out of the box. 
i.e. 

class Footer extends Component{
  _click(e){
     let Header = _thisManager.getThis('Header');
     Header.somefunction(" Wow some new data from footer event ");
  }
 render(){
      return(
      <div>
          <button onClick={this._click.bind(this)}> send data to header and call its render </button>
      </div>


      );
  }
}

I am sending json as a data in my application and it perfectly renders the desired components and i can invoke the render without any pub/sub or deep passing down the props to invoke a parent method with a changing this.setState to cause re-render.

So far the application works fine and i am also loving its simplicity too. Kindly throw light on this technique pros and cons

Regards

EDIT:

It is bad to call render so i changed it to another method to get more pros and cons of this setup.

Zenobia answered 26/2, 2016 at 10:46 Comment(3)
Although this works in a very limited use case like the example you provided, it interferes with the life cycle of react components. The render method is never meant to be invoked directly, and is not supposed to have inputs, this makes it impure. At the very least, expose a different public method which accepts whatever you are sending to render. In that method do setState which will invoke render implicitly. Finally instead of render call that method.Lucero
Thanks @HazardouS, certainly right but what about the technique, Basically i am not exposing it is the Reference through which render can be called and i guess setState will do the same effect plus what the damaging part in sending data to render function as argument ?Zenobia
SO app is not very good for long explanations :). Please read about pure render method assumption in React. Also React batches renders, which is why it shouldn't be invoked directly.Lucero
F
1

Two main concerns with this setup:
1. You should never call react lifecycle methods directly
2. Backdoors into components are a bad idea, which destroy react's maintainability

Ad 1: If you invoke render() (or any other react method) directly, react probably does not call componentDidMount(), componentDidUpdate()` and other lifecycle methods in the component tree.

Dangers are:

  • Many designs with react component rely heavily on the lifecycle methods being fired: getInitialState(), componentWillReceiveProps(), shouldComponentUpdate(), componentDidMount(), etc etc. If you call render() directly, many components will likely break or show strange behaviour.
  • You run the risk of breaking react's difference engine: through life-cycle management, react keeps a virtual copy of DOM in its (internal) memory. To work correctly, the integrity of this copy if vital to react's working.

Better would be (but still in violation of my second point):

  • Include a different method inside the component.
  • Which has a setState() if you want to re-render.
  • And call that method from the outside.

Ad 2. A direct reference to a mounted component (as your thisManager does) has some additional risks. React's designs and limitations are there for a reason: to maintain unidirectional flow and component hierarchy with props and state, to make things easy to maintain.

If you break this pattern - by building a backdoor into a component, which allows manipulation of state - you break this design principle of react. It is a quick shortcut, but is sure to cause great pain and frustation when your app grows.

As far as I know, the only acceptable exceptions to this rule are:

  • Methods inside a component that respond to ajax call results, to update state (e.g. after fetching data from server)
  • Methods inside a component to handle triggers from its direct descendent children components (e.g. run validation on form after a child button has been clicked)

So if you want to use it for that purpose, then you should be fine. Word of warning: the standard react approach guards random access to components, because the invoking component or method needs to have a reference to the component. In both examples such a reference is available.
In your setup, ANY outside piece of code could lookup the ref to the "header" in your table, and call the method which updates state. With such indirect reference, and no way of telling which source actually called your component, your code is likely to become much harder to debug/ maintain.

Fourdimensional answered 26/2, 2016 at 13:10 Comment(3)
Thanks @Fourdimensional , so any other method which can just cause this.setState will be fine ?Zenobia
right thanks, i edited the question , now wondering why would this break because essentially i we want is to reRender the component with new Data so the last thing was to subscribe to some event and do it , where as what i found easy is rather then publishing an event i send the data straight to my function which can do this.setState ? what do you say?Zenobia
See ad 2 in my answer: I would still advise against any indirect backdoor into component: best to stick to standard react: send props from parent to component, initiate listeners from inside component.Fourdimensional

© 2022 - 2024 — McMap. All rights reserved.