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.
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>
);
}