Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call function every time any component updates (including route change)

Tags:

reactjs

Is it possible to call a function every time anything in React updates?

A little more context: I want to use the full capabilities of EQCSS in React. I've tried 2 NPM packages, but they didn't seem to do what I expected exactly.

What I would like to do, is every time any component updates, I want to call EQCSS.apply().

I've tried calling in componentDidUpdate but this doesn't seem to work when changing pages (using react-router) for example.

import React from "react";
import ReactDOM from "react-dom";

import { Router, Route, browserHistory } from "react-router";

import { MainLayout } from "./layouts";
import {
    HomePage,
    RankingsPage
} from "./routes";

class App extends React.Component {
    render() {
        return (
            <Router history={browserHistory}>
                <Route component={MainLayout}>
                    <Route path="/" component={HomePage} />
                    <Route path="/rankings" component={RankingsPage} />
                </Route>
            </Router>
        );
    }

    componentDidUpdate() {
        console.log("COMPONENT UPDATED");
        // This does not get called when changing pages.
    }
}

// Render the main component into the dom
ReactDOM.render(<App />, document.getElementById("app"));
like image 517
Nsevens Avatar asked Apr 10 '26 01:04

Nsevens


1 Answers

Here are three different ways you could achieve this:


  1. React Context

    • Demo: http://codepen.io/PiotrBerebecki/pen/GjvzdJ
    • React docs: https://facebook.github.io/react/docs/context.html

  1. MutationObserver

Wrap your main parent component in the MutationObserver. More info here:

  • https://developer.mozilla.org/en/docs/Web/API/MutationObserver
  • https://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/

MutationObserver will track any changes to the actual DOM tree.


  1. Callback functions passed vie props

I've demonstrated another approach here: https://codepen.io/PiotrBerebecki/pen/wzqQkx. Please check the console which logs messages every time something has happened on the page, including Route changes, or re-renders of deeply nested children components. This relies however on a callback passed down through the child hierarchy via props.

Ad 1. Context demo code:

class App extends React.Component {
  static childContextTypes = {
    data: React.PropTypes.bool
  };

  getChildContext() {
    return {data: true};
  }
  render() {
    return (
      <div>
        <h1>Parent</h1>
        <Child />
      </div>
    )
  }
}


class Child extends React.Component {       
  static contextTypes = {
    data: React.PropTypes.bool
  };

  render() {
    console.log('in Child', this.context.data)
    return (
      <div>
        <h3>Child</h3>
        <Grandchild />
      </div>
    );
  }
}


class Grandchild extends React.Component {       
  static contextTypes = {
    data: React.PropTypes.bool
  };

  render() {
    console.log('in Grandchild', this.context.data)
    return (
      <div>
        <h6>Grandchild</h6>
      </div>
    );
  }
}


ReactDOM.render(
  <App />,
  document.getElementById('app')
);

Ad 3. Callback demo code:

var MainLayout = React.createClass({
  doSomething: function() {
    console.log('something happened');
  },

  render: function() {
    this.doSomething();
    const childrenWithProps = React.Children.map(this.props.children,
     (child) => React.cloneElement(child, {
       doSomething: this.doSomething
     })
    );

    return (
      <div className="app">
        <header className="primary-header"></header>
        <aside className="primary-aside">
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/users">Users</Link></li>
            <li><Link to="/widgets">Widgets</Link></li>
          </ul>
        </aside>
        <main>
          {childrenWithProps}
        </main>
      </div>
      )
  }
})

var Home = React.createClass({
  getInitialState: function() {
    return {
      counter: 1
    };
  },

  componentDidUpdate: function() {
    this.props.doSomething()
  },

  changeCounter: function() {
    this.setState({
      counter: this.state.counter + 1
    });
  },

  render: function() {
    return (
      <div>
        <button onClick={this.changeCounter}>Click me</button>
        <br />
        {this.state.counter}
      </div>
    );
  }
})

var SearchLayout = React.createClass({
  render: function() {
    const childrenWithProps = React.Children.map(this.props.children,
      (child) => React.cloneElement(child, {
       doSomething: this.props.doSomething
     })
    );

    return (
      <div className="search">
        <header className="search-header"></header>
        <div className="results">
          {childrenWithProps}
        </div>
        <div className="search-footer pagination"></div>
      </div>
      )
  }
})

var UserList = React.createClass({
  getInitialState: function() {
    return {
      counter: 1
    };
  },

  componentDidUpdate: function() {
    this.props.doSomething()
  },

  changeCounter: function() {
    this.setState({
      counter: this.state.counter + 1
    });
  },

  render: function() {
    return (
      <div>
        User List
        <br />
        <button onClick={this.changeCounter}>Click me</button>
        <br />
        {this.state.counter}
      </div>
    );
  }
});

var WidgetList = React.createClass({
  getInitialState: function() {
    return {
      counter: 1
    };
  },

  componentDidUpdate: function() {
    this.props.doSomething()
  },

  changeCounter: function() {
    this.setState({
      counter: this.state.counter + 1
    });
  },

  render: function() {
    return (
      <div>
        Widgets
        <br />
        <button onClick={this.changeCounter}>Click me</button>
        <br />
        {this.state.counter}
      </div>
    );
  }
})



ReactDOM.render((
  <Router>
    <Route path="/" component={MainLayout}>
      <IndexRoute component={Home} />
      <Route component={SearchLayout}>
        <Route path="users" component={UserList} />
        <Route path="widgets" component={WidgetList} />
      </Route> 
    </Route>
  </Router>
), document.getElementById('root'))
like image 60
Piotr Berebecki Avatar answered Apr 11 '26 14:04

Piotr Berebecki



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!