I'm playing around with Flux and it's working great but I'm now trying to use it in a way where a flux store determines a single point of truth or state for all child components.
I'm basically trying to implement an auth system, which determines the auth state of the current user without having to update the state of each component individually (there are lots of them) by doing something like this :
state = {
authenticated: AuthStateStore.getState(),
uid: AuthStateStore.getUid(),
token: AuthStateStore.getToken(),
username: AuthStateStore.getUsername(),
avatar: AuthStateStore.getAvatar(),
errors: []
}
I thought I could set the state on the main parent <App/>
component (from which everything else is rendered as children) and then pass the state down as props to all children. This indeed works - {this.props.state.authenticated}
will show the correct state from <App/>
in the children - but props are immutable, meaning that when the state of <App />
is updated via a flux store, none of the props being sent to children are updated with the new values from <App/>
.
Is there any way to achieve what I'm trying to do here or do I have to set the state from the flux store in every component that requires the info in this way?
EDIT: how I'm passing the state down from <App/>
(I've trimmed the fat so they are more readable).
App (Main parent)
class App extends Component {
state = {
authenticated: AuthStateStore.getState(),
uid: AuthStateStore.getUid(),
token: AuthStateStore.getToken(),
username: AuthStateStore.getUsername(),
avatar: AuthStateStore.getAvatar()
}
render() {
return (
<BrowserRouter>
<div>
<Sidebar state={this.state}/>
<Switch>
<Route exact path="/" component={Home} state={this.state}/>
<Route exact path="/signup" component={Signup} state={this.state}/>
<Route path="/activate/([\da-f]+)" component={Activate}/>
.......
</Switch>
<Footer />
</div>
</BrowserRouter>
);
}
}
Sidebar (first child)
class Sidebar extends Component {
render() {
return (
<div className="sidebar">
<Navigation state={this.props.state}/>
<Search/>
</div>
);
}
}
Navigation (Second child)
class Navigation extends Component {
render() {
if (this.props.state.authenticated === "true") {
return (
<div>
<p>Authenticated content here</p>
</div>
);
} else {
return (
<div>
<p>Non-authenticated content here</p>
</div>
);
}
}
}
The above code works on first load, but if the state of <App/>
changes via the flux store, this isn't reflected in the children unless you do a full page reload (which we obviously don't want to do).
Further Edit...
Just to be concise, right now I have everything working by doing the following in each child component that requires the Auth state info (again fat trimmed for readability)...
class Navigation extends Component {
state = {
authenticated: AuthStateStore.getState(),
uid: AuthStateStore.getUid(),
token: AuthStateStore.getToken(),
username: AuthStateStore.getUsername(),
avatar: AuthStateStore.getAvatar()
}
componentWillMount() {
AuthStateStore.on("change", () => {
this.setState({
authenticated: AuthStateStore.getState(),
uid: AuthStateStore.getUid(),
token: AuthStateStore.getToken(),
username: AuthStateStore.getUsername(),
avatar: AuthStateStore.getAvatar()
});
});
}
render() {
if (this.state.authenticated === "true") {
return (
<div>
<p>Authenticated content here</p>
</div>
);
} else {
return (
<div>
<p>Non-authenticated content here</p>
</div>
);
}
}
}
... but in the spirit of DNRY, and trying to avoid any potential pitfalls due to forgetting to call or set a state in a component somewhere, I'm looking for an alternative 'one-stop-shop' solution which I was hoping flux could provide.