React router, how to restore state after browser back button?

Viewed 32675

I'm trying to figure out how to make my React SPA app maintain state when the user navigates with the back/forward browser buttons. E.g. the user is filling a form, then he moves back and forward and the form is automatically restored.

I reviewed many JavaScript routers around but none seem to address this issue properly... (or even at all).

Currently I'm using React Router 4 and this is the pattern I'm adopting:

  1. when programmatically leaving the page, I first save current page's state with history.replace(this.state); then I move out of the page with history.push(newLocation)
  2. when a page component is created (componentWillMount) I check for this.props.location.state !== undefined and if so, I restore it with this.setState(this.props.location.state)

My question is: is the above pattern correct? suggested? Is there any better and widely adopted way?

4 Answers

Use getDerivedStateFromProps and check if there has been a change in value of props.location.key to restore states only when the user navigates with browser buttons.

 static getDerivedStateFromProps(props,states){
         if ((typeof props.location !== 'undefined') &&
             (typeof props.location.state !== 'undefined') &&
              (states.location_key !== props.location.key)) {
             let newState = props.location.state;
             newState.location_key = props.location.key;
             return newState;
         }
    }

You can also use the session storage instead of state. Just replace the useState() function and use the useSessionStorage().

For example:


const [myState, setMyState] = useSessionStorage("state_key", "initial_value")


const handleSomeChangeOnState = (event: any) => {
  setMyState("new_value")
}

i thought about this problem too, For example you have multi-step form and you want to navigate between steps by browser controls too. As result you have to use router for storing activeStep, but you don't want to share it in browser URL. The solution is store step in router state:

const location = useLocation();
const history = useHistory();

const onSubmit = useCallback(() => {
  history.push({
    ..location,
    state: {
      activeStep: 1
    }
  });
}, [...]);

As result your step will restore event after page refreshing, but you can't share step to other user. It's perfect.

The next problem is store form data between steps. We can't store it in router state, too. I've chosen for it sessionStorage. It has similar properties like router state. As result you have to update session storage fro example on submit and retrieve data on init form:

const location = useLocation();
const history = useHistory();
const [initData] = useState(getFormDataFromStorage());

const onSubmit = useCallback(() => {
  saveFormDataToStorage(...);
  history.push({
    ..location,
    state: {
      activeStep: 1
    }
  });
}, [...]);

i prepared the demo and GitHub where you can test it and details you can find here (RU)

Related