Search code examples
reactjstypescript

Unable to update child functional component in Typescript React


Sorry if i am not explaining well, I am new to typescript programming. I am trying to develop a from for creating and editing order forms. I wanted to reuse the code for the form in other modules, so I tried to make this part a functional component to be loaded by the parent form. I am passing the form data to the child using an interface object as props. I have the same interface object as part of the state of the parent form. I can successfully update the state using a button on the parent form. I can see the props of the child component update to match. The elements of the child component are not updating. It seems the state of the child component isn't updating when the object is reinitialized. I don't understand why as I am passing the props using the spread operator.

I simplified my code into a single file to far less parameters and made dummy promises to take the place of the methods I was loading the data with. This code is giving the same results as the full program so it should be sufficient for debugging:

import * as React from 'react';
import { useState } from 'react';

export interface IChildFormParameters{
  Name?: string;
  Description?: string;
  CheckBoxState?: boolean;
}

const ChildFunctionalComponent : React.FC<IChildFormParameters> = (props:IChildFormParameters) => {

  const [checked, setChecked] = useState({
      CheckBoxState: props.CheckBoxState,
  });

  const [state, setState] = useState({
      Name: props.Name,
      Description: props.Description,
  });

  const handleChecked = (e:React.ChangeEvent<HTMLInputElement>):void => {
      setChecked({
          ...checked,
          [e.target.name]: e.target.checked,
      }); 
  }

  const handleChange = (e:React.ChangeEvent<HTMLInputElement>):void => {
      setState({
        ...state,
        [e.target.name]: e.target.value,
      })
  } 

  console.log(props);
  console.log((state.Name ?? ' ') + ', ' + (state.Description ?? ' ') + ', ' + checked.CheckBoxState);
  
      return (
          <div>
              <input name="CheckBoxState" type="checkbox" checked={checked.CheckBoxState} onChange={handleChecked}/>
              <input name="Name" type="text" value={state.Name} onChange={handleChange}/>
              <input name="Description" type="text" value={state.Description} onChange={handleChange}/>
          </div>
      )
}

export default function Childcomponentfunction(props:any): JSX.Element {
  
    const [state,setState] = useState({
      formParams: {CheckBoxState:true, Name:'Joe', Description:'Somebody'} as IChildFormParameters,
    });

    async function FillForm(){
      const params = await LoadForm();
      console.log(params);
      setState({ "formParams": params});
    }

    async function LoadForm() : Promise<IChildFormParameters> {
      return {CheckBoxState:false, Name:'Paul', Description:'Nobody'} as IChildFormParameters;
    }

    return (
      <section>
        <div>
          <h1>  Component Below: </h1>
          <h2>  <ChildFunctionalComponent { ... state.formParams }/>  </h2>
          <h3>  <button name="LoadForm" type="submit" onClick={FillForm}>Submit Form</button></h3>
        </div>
      </section>
    );
}

When the form loads, Childcomponentfunction state parameters are logged to the console as: {CheckBoxState: true, Name: 'Joe', Description: 'Somebody'} I can see the state of the ChildFunctionalComponent is the same logged to the console: Joe, Somebody, true

When I click the "Load Form" button, Childcomponentfunction state parameters are updated as intended: {CheckBoxState: false, Name: 'Paul', Description: 'Nobody'} The ChildFunctionalComponent props are updated to match per the console log. The ChildFunctionalComponent state is not changed


Solution

  • initialState: The value you want the state to be initially. It can be a value of any type, but there is a special behavior for functions. This argument is ignored after the initial render. - link

    You need to have an effect that watch props changes to update checked and state

    React.useEffect(() => {
        props.CheckBoxState !== undefined &&
          setChecked({ ...checked, CheckBoxState: props.CheckBoxState });
        setState({ ...state, Name: props.Name, Description: props.Description });
    }, [props]);
    

    Working sample: https://stackblitz.com/edit/vitejs-vite-ckzpwe?file=src%2FApp.tsx&terminal=dev