3

I have a simple code here. I am using react-router and I can't get why the state of all pages are fixed to the one of the initial render and the inputfields share all the value?

import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Page from "./Page";

export default function App() {
  return (
    <Router>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
      <Link to="/users">Users</Link>

      <Switch>
        <Route path="/about">
          <Page direction="left" />
        </Route>
        <Route path="/users">
          <Page direction="right" />
        </Route>
        <Route path="/">
          <Page direction="up" />
        </Route>
      </Switch>
    </Router>
  );
}

import React, { useReducer } from "react";

export default function Page({direction}){
  let reducer = (state, action) => state;
  let [state, dispatch] = useReducer(reducer, {direction});
  return (<div><input type="text"/>{state.direction}</div>)
}
1
  • The code on given link is not working Commented Sep 26, 2020 at 11:09

3 Answers 3

4

Although the other answer has provided a solution, i think its missing an explanation of the cause of the issue.

Problem:

So the problem is because of how React updates the UI in an optimized way. In your case, React is re-using your Page component. Because Page component is already in the DOM, instead of creating a new one, React re-uses the same instance of the Page component and only changes the data that has changed, in your case, only direction prop changes.

Adding the key prop works because this allows React to differentiate between different instances of Page component.

Alternative Solution:

When React re-uses the Page component, uderlying instance of the Page component stays the same but the different props are passed to the underlying instance.

In your code, since you are not updating the state when direction prop changes, you don't see a different state value.

To fix this, you can use useEffect hook that dispatches an action to update the state whenever direction prop changes.

Changing the code in Page.js file as shown below will fix the problem:

function reducer(state, action) {
  switch (action.type) {
    case "DIRECTION_CHANGE":
      return { ...state, direction: action.payload };
    default:
      return state;
  }
}

export default function Page({ direction }) {
  const [state, dispatch] = useReducer(reducer, { direction });

  useEffect(() => {
    dispatch({ type: "DIRECTION_CHANGE", payload: direction });
  }, [direction]);

  return (
    <div>
      <input type="text" />
      {JSON.stringify(state)}
    </div>
  );
}

For further details, see: React - Reconciliation

Sign up to request clarification or add additional context in comments.

Comments

2

You can fix it by adding a property key to each Page component

<Switch>
  <Route  path="/about">
    <Page key="/about" direction="left" />
  </Route>
  <Route path="/users">
    <Page key="/users"  direction="right" />
  </Route>
  <Route  path="/">
    <Page key="/" direction="up" />
  </Route>
</Switch>;

This way you will exploit the diffing algorithms https://reactjs.org/docs/reconciliation.html#keys

1 Comment

Weird behavior, I was thinking that react does this automatically. Well, thanks.
0

This works for me for your Page component:

import React from "react";

export default function Page(props) {

  return (
    <div>
      <input type="text" />
      {JSON.stringify(props)}
    </div>
  );
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.