In early days of web, I use flash state a lot to send message after redirection. It's easy to do it using server side code, because we can use session & cookie. Now that we use SPA everywhere, navigation happen in client-side using browser history API.
If you're using React Router (v4), you might know they have component to do a redirection. So, how do we implement flash state with React Router?
This might be the one you have in mind
render() { if (this.state.redirect) { return ( <Redirect to={{ pathname: '/', state: { message: 'Message from other page' } }} /> ); }}
Except this won't work. Flash state should dissapear after next request (refresh or navigation). React router uses browser history. It persist state change on refresh and navigation. It won't dissapear, unless you replace the state manually.
React Lifecycle to the Rescue
The solution I arrived is not the best or even elegant, but it works well and simple enough to use. The answer is componentDidMount
.
As you might know, on every route change, your component will be (re)mounted and componentDidMount
lifecycle will be executed.
Why is this useful?
By relying on this behavior, we can read the flash state on componentDidMount
, and remove them afterwards. Removing it manually is prone to error, this way we remove them just after reading the state
// FlashState.jsclass FlashState { constructor() { this.state = {}; } get(key) { const value = this.state[key]; this.state[key] = null; return value; } set(key, value) { this.state[key] = value; }}export default new FlashState();
Because we export a singleton, any other component that imports FlashState
will get the same instance (and also the state inside it). Now we can use it alongside React Router Redirect component.
// EditPostPage.jshandlePublishPost = () => { FlashState.set('message', 'Post published'); this.setState({ published: true });}render() { if (this.state.published) { return <Redirect to='/' /> }}
In home component, we read state inside componentDidMount
and render them if exist.
// HomePage.jscomponentDidMount() { this.setState({ message: FlashState.get('message') });}render() { return ( <App> {this.state.message && <div className="flash">{this.state.message}</div>} </App> );}
I could create HoC (or Flash component with render prop) to remove componentDidMount
boilerplate, but for most cases this is enough.