Trispo
Trispo

Reputation: 59

Avoid pass again prop to child component after useState in parent component in React

i want to avoid pass again props to child component after useState in parent component in React.

Let me explain better with the following example

Parent Component:

export default function ParentComponent() {

   const [isDirty, setIsDirty] = useState(false);
   const [defaultValue, setDefaultValue] = useState('');
  
   const myInputFieldConfiguration = {
    field: 'myfield1',
    type: 'text',
    isDisabled: false,
    value: defaultValue
   };
   
   useEffect(() => {
     getDefaultData()
   }, []);
   
  const getDefaultData = async () => {
    const defaultDataResponse = await api.getDefaultData();
    setDefaultValue(defaultValueDataResponse);
  }

   return (
    <>
       <ChildComponent 
          inputFieldConfiguration={myInputFieldConfiguration}
          onIsDirty={isDirty => setIsDirty(isDirty)}
       />
    <>
   ) 
}

Child Component:

export default function ChildComponent(props) {
   
  function handleChange(event) {
  if(event.target.value !== props.inputFieldConfiguration.value) {
          props.onIsDirty(true);
   }
  }

   return (
    <>
       <input 
          type={props.inputFieldConfiguration.type}
          disabled={props.inputFieldConfiguration.disabled}
          value={props.inputFieldConfiguration.value}
          onChange={handleChange}
       />
    <>
   ) 
}

In the above example I call an api to get a default value, i set this value in a state and then i declare a varible myInputFieldConfiguration that keep this default value in the value property. Then I pass to the ChildComponent the value - myInputFieldConfiguration - as prop and i use it inside that component to prefill and configure an input field. In the ChildComponent when i edit the value of that input field i call the onIsDirtyMethod that in the ParentComponent set the isDirtyState. The issue is there, when the state is been setted the ParentComponent re-render itself and pass again the original myInputFieldConfiguration as props to the ChildComponent, the consequence is that the input field in the ChildComponent change is value again to the default myInputFieldConfiguration.value value.

How can i solve this issue?

Thank you everyone in advice.

Upvotes: 0

Views: 94

Answers (1)

RiQts
RiQts

Reputation: 114

Every time the ParentComponent re-rendered (for instance, when the isDirty state changed), it passed the same inputFieldConfiguration object to the ChildComponent. Since this configuration object contained the defaultValue (which did not change after the initial API call), the ChildComponent always received the initial defaultValue, overriding any changes made to the input field.

This behavior is a direct consequence of the one-way data flow in React. The ChildComponent did not have its own state to manage the input value; it relied entirely on the props passed from the ParentComponent. Therefore, any re-render of the ParentComponent effectively reset the input field's value in the ChildComponent to the initial default value.

To resolve this, the ChildComponent needs to manage its own state for the input field value.

export default function ParentComponent() {
   const [isDirty, setIsDirty] = useState(false);
   const [defaultValue, setDefaultValue] = useState('');

   useEffect(() => {
     getDefaultData();
   }, []);

   const getDefaultData = async () => {
     const defaultDataResponse = await api.getDefaultData();
     setDefaultValue(defaultDataResponse); // Make sure this variable name is correct
   }

   return (
    <>
       <ChildComponent 
          defaultValue={defaultValue}
          onIsDirty={setIsDirty}
       />
    </>
   ) 
}
export default function ChildComponent(props) {
   const [inputValue, setInputValue] = useState(props.defaultValue);

   //ensures that if the defaultValue prop changes,
   // it updates its internal state. This handles cases where the default
   // value might change due to some external factors.
   useEffect(() => {
     setInputValue(props.defaultValue);
   }, [props.defaultValue]);

   function handleChange(event) {
      const newValue = event.target.value;
      if(newValue !== props.defaultValue) {
          props.onIsDirty(true);
      }
      setInputValue(newValue);
   }

   return (
    <>
       <input 
          type="text"
          disabled={false}
          value={inputValue}
          onChange={handleChange}
       />
    </>
   ) 
}

The ChildComponent now has its own state for inputValue. It initializes with the defaultValue passed from the parent but then manages its own state independently.

Upvotes: 1

Related Questions