React Router and Flash State

Old Technique in a Modern Tech Stack

  • Published at 
  • 3 mins reading time

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.js
class 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.js
handlePublishPost = () => {
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.js
componentDidMount() {
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.

Categorized under

Webmentions

If you think this article is helpful
© 2023 Fatih Kalifa. All rights reserved.