Red Apple
Red Apple

Reputation: 109

React: How to re-render a route on state change within App.js

I have a React app which renders two routes: a CreatorView and a ViewerView routed under App.js (both composed of smaller components). The problem I have is when I have both routes open in two different tabs in the browser and I click on a button inside a component in CreatorView, I use callbacks to change/set the state of the App.js (first I pass state from the component to CreatorView, then from CreatorView I pass the state to App.js and set state of App.js). Then finally, I pass the new state of App.js to ViewerView, which is a routed component, because I want it to re-render to display something else. In theory, when I click on the button in CreatorView, App.js re-renders (due to state change), and also the routes (both CreatorView and ViewerView). However, only CreatorView seems to re-render with this state change. ViewerView doesn't seem to be updating/re-rendering even when its state is being changed through App.js. In other words, how do I make ViewerView re-render, which is already open in the other tab, once I click on the button in CreatorView in the other tab. I have provided my file hierarchy and my code below:

 ┣ 📂elements
 ┃ ┗ 📜CreatorControlPanel.js #contains the button which will trigger set state in App.js
 ┃ ┗ 📜CreatorHeader.js
 ┃ ┗ 📜ViewerControlPanel.js
 ┃ ┗ 📜ViewerHeader.js
 ┣ 📂views
 ┃ ┗ 📜CreatorView.js #contains CreatorControlPanel & CreatorHeader
 ┃ ┗ 📜ViewerView.js #contains ViewerControlPanel & ViewerHeader

App.js

class App extends Component {
    constructor() {
    super()
    this.state = {
        click: false
    }
    this.setClicked = this.setClicked.bind(this)
    }
    
    setClicked() {
        this.setState({click: true})
    }
    
    render() {
        return (
            <Router>
                <Switch>
                    <Route path="/:snippetId/creator" exact render={() => ( <CreatorView clickedCallback={this.setClicked}/> )}  />
                    <Route path="/:snippetId"   render={() => ( <ViewerView clicked={this.state.click}/> )} />
                </Switch>
            </Router>
        );
    }
}

CreatorControlPanel.js

import { MyContext } from '../views/MyProvider';

class CreatorControlPanel extends Component { 
constructor(props) {
    state = {...}
    
    handleClick() {
       this.props.clickCallback();
    }

    render() {
        return(
           <Button onClick={this.handleClick}>Click me</Button>{' '}
        )
    }
}

CreatorView.js

import CreatorControlPanel from '../elements/CreatorControlPanel';

class CreatorView extends Component {
      state = { ... }

      setClicked() {
        this.props.clickedCallback();
      }

      render() {
          return(
             <CreatorControlPanel clickCallback={this.setClicked}/>/>
          )
      }
}

ViewerView.js

class ViewerView extends Component {
    
    state = {
         click: null
    }     
    
    componentDidMount() {
        this.setState({click: this.props.clicked})
    }

    render() {
      if(this.state.click === false){
        return (
            <div>hi, hello</div>
        )
      } else return (
            <div>clicked</div> #ViewerView won't re-render to display this once clicked
        )
    } 

Upvotes: 1

Views: 661

Answers (2)

Someone Special
Someone Special

Reputation: 13588

You can use use-persisted-state to persist your view to localStorage

import createPersistedState from 'use-persisted-state';
const useView = createPersistedState('click');

const CreatorView = () => {
  const [click, setClick] = useView(false);

  return (<div>{click}</div>)
};


   const ViewerView = () => {
      const [click, setClick] = useView(false);
    
      return (<div>{click}</div>)
   }

The state is shared by all your windows and tabs using the same key. Hence if you setClick to true in CreatorView in Window tab 1, it will be reflected in ViewerView in window tab 2 as well.

Since it uses localStorage, you can store some events but I don't recommend you to save big data. It's not designed for that.

Upvotes: 1

Pavel Alekseev
Pavel Alekseev

Reputation: 1222

You can't do this in simple way because when you open your project in different tabs they behave like different instances so you can't update one instance and see the changes in the other instances.

To achieve this you can use WebSocket.

Upvotes: 0

Related Questions