Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Retrieving latest state from functional child component using callbacks

Coming from Vue and diving into React I seem to struggle with the concept of the hooks / component lifecycle and data flow. Where in Vue I could solve my issues using v-model in React I struggle to do so. Basically:

What I intend to do is : have a parent component which is a form. This component will have several child components where each are fragments of the form data. Each child component manages its own state. The parent component has a submit button that should be able to retrieve the values from all child components.

In a nutshell, my approach is: have a functional component to manage part of said form using state hooks. This "form fragment"-component can broadcast a change event broadcastChange() containing the updated values of its inputs. In the parent I have a submit button which invokes this broadcastChange() event from the child using a ref.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

The problem I am running into is that I am always getting the default values of the child state. In the below example, I’d always be getting foo for property inputValue. If I were to add a submit button inside the child component to invoke broadcastChange() directly, I do get the latest values.

What am I overlooking here ? Also, if this is not the React way to manage this two-way communication, I’d gladly hear about alternatives.

Parent code:

function App() {
    const getChildChangesFn = useRef( null );
    const submitForm = e => {
        e.nativeEvent.preventDefault();
        getChildChangesFn.current(); // request changes from child component
    };
    const handleChange = data => {
        console.log( data ); // will always list { inputValue: "foo" }
    };
    return (
        <form>
            <child getChangeFn={ getChildChangesFn } onChange={ handleChange } />
            <button type="submit" onClick={ () => submitForm() }>Save</button>
        </form>
    );
}

Child code:

export default function Child( props ) {
    const [ inputValue, setInputValue ] = useState( "foo" );

    useEffect(() => {
        // invoke inner broadcastChange function when getChangeFn is triggered by parent
        props.getChangeFn.current = broadcastChange;
    }, []);

    const broadcastChange = () => {
        props.onChange({ inputValue });
    };

    render (
        <fieldset>
            <input
                type="text"
                value={ inputValue }
                onChange={ e => setInputValue( e.target.value ) }
            />
        </fieldset>
    );
}

>Solution :

You need to leave Vue behind and make your thinking more React-y. Rather than trying to manage your state changes imperitavely by ‘broadcasting’ them up, you need to ‘lift your state’ (as they say) in to your parent and pass it down to your children along with change handlers.

A simple example:

export default function App() {
  const [childState, setChildState] = useState(false);
  const onChildClick = () => setChildState((s) => !s);
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <ChildComponent childState={childState} onClickHandler={onChildClick} />
    </div>
  );
}


const ChildComponent = ({ childState, onClickHandler }) => {
  return (
    <button onClick={onClickHandler}>
      State is {childState ? "true" : "false"}
    </button>
  );
};

Sandbox here

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading