Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React hooks - state in useState() is not reset when route is changed

const Create = () => {
  console.log('rerender !!')
  const [parcelType, setParcelType] = useState('paper')
  console.log('parcelType =', parcelType)

  return (
    <Container onClick={() => setParcelType('plastic')}>
      <BookingList />
      <Card title="Business">
        <p>Header</p>
      </Card>
    </Container>
  )
}

export default Create

I want to change parcelType state to 'plastic' when click on Container in Create component. and I want to reset parcelType state to 'paper' when route is change ( Create component re-render ). But when component re-render state is not set to paper

For more details: CreateComponent is re-render when route is change in BookingList component

 const BookingList = props => {
  const { id } = props.match.params
  const containerStyle = useTranslateSpring('-100px', '0')

  const itemList = items.map((item, idx) => {
    const itemStyle = useTranslateSpring('-100px', '0', '0', 200 + 200 * idx)
    const url = `/booking/${item.id}/create`

    return (
      <ItemContainer
        onClick={() => props.history.push(url)}
        style={itemStyle}
        key={item.id}
        isactive={id === item.id}
      >
        {item.id}
      </ItemContainer>
    )
  })
  return <Container style={containerStyle}>{itemList}</Container>
}

export default withRouter(BookingList)

Create Component is render in route by routeTemplate

const Routes = () => (
 <Router basename={process.env.REACT_APP_BASE_URL}>
   <> 
    <RouteTemplate
    exact
    path="/booking/:id/create"
    component={Booking.create}
    title="Booking"
    />
   </>
 </Router>
)

and RouteTemplate is render Component wrapped by PageTemplate component

  const RouteTemplate = props => {
  const {
    component: Component,
    title,
    query,
    isAuthenticated,
    isLanding,
    ...rest
  } = props

  return (
    <Route
      {...rest}
      render={matchProps =>
        isAuthenticated ? (
          <PageTemplate title={title} isLanding={isLanding}>
            <Component {...matchProps} query={query} />
          </PageTemplate>
        ) : (
          <Redirect
            to={{
              pathname: '/',
              state: { from: props.location },
            }}
          />
        )
      }
    />
  )
}
like image 510
sarin udompanit Avatar asked Mar 20 '19 05:03

sarin udompanit


People also ask

Why does the React useState Hook not update immediately?

The answer: They're just queues setState , and React. useState create queues for React core to update the state object of a React component. So the process to update React state is asynchronous for performance reasons. That's why changes don't feel immediate.

How do you reset state of useState?

When you manage state using the useState hook, all you have to do is call your setState function passing it the initial state. Copied! const resetState = () => { setEmployee(initialState); }; With the useReducer hook, you have to add an action handler for the RESET state that returns the initial state.

How do I change state with useState Hook?

To update the state, call the state updater function with the new state setState(newState) . Alternatively, if you need to update the state based on the previous state, supply a callback function setState(prevState => newState) .

How do you reset the state variable in React?

Resetting States to Initial State Then we call useState in App to create the object state. Next, we create the clearState function to reset the state by calling the setState state setter function with a copy of the initialState . Making a copy lets React know that it has to update the state and re-render.


1 Answers

So I assume you want to reset component's state once route is changed.

This should happen wherever you use functional component + hooks or class-based component with explicit this.state. It's how React works under the hood.

  1. You already have <Create> rendered at the page
  2. Once route is changed <Route> tries to render <Create> element
  3. React sees there is already existing <Create> element and tries to update that instead of re-creating(typically update is much more efficient than re-creating). That's why state is not reset - since it should not reset for updates.

There are different way to handle that.

If such a case happen outside react-router's <Route> I'd suggest use key prop to reset state. But for <Route> it would mean replacing more clear/straightforward <Route path="..." component={Create} /> with more verboose <Route path="..." render={({match}) => <Create match={match} key={match.params.id} />}

So instead let's apply useEffect hook to reset state once props.match.params.id is changed:

const Create = ({ match: {params: {id} } }) => {    

  useEffect(() => {
    setParcelType('paper');
  }, [id]);

That should be equal to class-based

state = {
  typeOfWhatEver: 'paper'
};

componentDidUpdate(prevProps) {
  if(prevProps.match.params.id !== this.props.match.params.id) {
    this.setState({
      typeOfWhatEver: 'paper'
    });
  }
}
like image 88
skyboyer Avatar answered Nov 09 '22 08:11

skyboyer