How can I reset a react component including all transitively reachable state?

StackOverflow https://stackoverflow.com/questions/21749798

  •  11-10-2022
  •  | 
  •  

Question

I occasionally have react components that are conceptually stateful which I want to reset. The ideal behavior would be equivalent to removing the old component and readding a new, pristine component.

React provides a method setState which allows setting the components own explicit state, but that excludes implicit state such as browser focus and form state, and it also excludes the state of its children. Catching all that indirect state can be a tricky task, and I'd prefer to solve it rigorously and completely rather that playing whack-a-mole with every new bit of surprising state.

Is there an API or pattern to do this?

Edit: I made a trivial example demonstrating the this.replaceState(this.getInitialState()) approach and contrasting it with the this.setState(this.getInitialState()) approach: jsfiddle - replaceState is more robust.

Was it helpful?

Solution

To ensure that the implicit browser state you mention and state of children is reset, you can add a key attribute to the root-level component returned by render; when it changes, that component will be thrown away and created from scratch.

render: function() {
    // ...
    return <div key={uniqueId}>
        {children}
    </div>;
}

There's no shortcut to reset the individual component's local state.

OTHER TIPS

Adding a key attribute to the element that you need to reinitialize, will reload it every time the props or state associate to the element change.

key={new Date().getTime()}

Here is an example:

render() {
  const items = (this.props.resources) || [];
  const totalNumberOfItems = (this.props.resources.noOfItems) || 0;

  return (
    <div className="items-container">
      <PaginationContainer
        key={new Date().getTime()}
        totalNumberOfItems={totalNumberOfItems}
        items={items}
        onPageChange={this.onPageChange}
      />
    </div>
  );
}

You should actually avoid replaceState and use setState instead.

The docs say that replaceState "may be removed entirely in a future version of React." I think it will most definitely be removed because replaceState doesn't really jive with the philosophy of React. It facilitates making a React component begin to feel kinda swiss knife-y. This grates against the natural growth of a React component of becoming smaller, and more purpose-made.

In React, if you have to err on generalization or specialization: aim for specialization. As a corollary, the state tree for your component should have a certain parsimony (it's fine to tastefully break this rule if you're scaffolding out a brand-spanking new product though).

Anyway this is how you do it. Similar to Ben's (accepted) answer above, but like this:

this.setState(this.getInitialState());

Also (like Ben also said) in order to reset the "browser state" you need to remove that DOM node. Harness the power of the vdom and use a new key prop for that component. The new render will replace that component wholesale.

Reference: https://facebook.github.io/react/docs/component-api.html#replacestate

The approach where you add a key property to the element and control its value from the parent works correctly. Here is an example of how you use a component to reset itself.

The key is controlled in the parent element, but the function that updates the key is passed as a prop to the main element. That way, the button that resets a form can reside in the form component itself.

const InnerForm = (props) => {
  const { resetForm } = props;
  const [value, setValue] = useState('initialValue');

  return (
    <>
      Value: {value}
      <button onClick={() => { setValue('newValue'); }}>
        Change Value
      </button>
      <button onClick={resetForm}>
        Reset Form
      </button>
    </>
  );
};

export const App = (props) => {
  const [resetHeuristicKey, setResetHeuristicKey] = useState(false);
  const resetForm = () => setResetHeuristicKey(!resetHeuristicKey);
  return (
    <>
      <h1>Form</h1>
      <InnerForm key={resetHeuristicKey} resetForm={resetForm} />
    </>
  );
};

Maybe you can use the method reset() of the form:

import { useRef } from 'react';

interface Props {
    data: string;
}

function Demo(props: Props) {
    const formRef = useRef<HTMLFormElement | null>(null);

    function resetHandler() {
        formRef.current?.reset();
    }

    return(
        <form ref={formRef}>
            <input defaultValue={props.data}/>
            <button onClick={resetHandler}>reset</button>
        </form>
    );
}

Example code (reset the MyFormComponent and it's state after submitted successfully):

function render() {
    const [formkey, setFormkey] = useState( Date.now() )

    return <>
        <MyFormComponent key={formkey} handleSubmitted={()=>{
            setFormkey( Date.now() )
        }}/>
    </>
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top