skip to Main Content

I am new to React, and I am having the following issue:

When I get a value from redux, I am creating a clone of it and saving it to useState so I can modify it without affecting the original value until I press save, which then sends the value to redux to be updated in my store. The problem I am having, is in the function saveProject the value doesn’t change name within the object, as it is still the original value. However, in the effect, the correct value is displayed. I am not sure why, what would be causing this?

Here is an example (Open the console to see output).

Note: I have tried changing defaultValue to value, but then I cannot type into the input box which leads me to believe maybe there is something else going on?

const [isEditDialogOpen, setIsEditDialogOpen] = useState(true);

return (
  {isEditDialogOpen && <EditProjectDialog onClose={() => setIsEditDialogOpen(false)} />}
)
// Simplified: <Dialog />
export default ({ children }) => {
  const DialogOutput = <div>{children}</div>

  return <>{createPortal(DialogOutput,document.body)}</>
}
// Edit dialog: <EditProjectDialog />
export default () => {
  // Original value from redux
  const active = useSelector(selectActiveProject);
  // The value I want to track
  const [project, setProject] = useState<Project>(Object.assign({}, active));
  // The value should be the updated value here (but shows the original)
  const saveProject = () => console.log(project);
  // Sets the value when the input is typed into
  const nameChange = (e: BaseSyntheticEvent) => setProject({ ...project, name: e.target.value });

  // Prints the correct value from the input
  useEffect(() => console.log('useEffect', project), [project]);

  return (
    <Dialog>
      <input defaultValue={project.name} onChange={nameChange} />
      <button onClick={saveProject}>Save</button>
    </Dialog>
  );
}

Output

2

Answers


  1. I am not so sure that this isn’t working as much as that the saveProject function has the initial version of project. To check if this is the problem I would make a useCallback function:

    useCallback(() => console.log(project), [project])
    

    Other than that typically you want to use the value field instead of the defaultValue because the defaultValue field makes this an uncontrolled component and it doesn’t seem like that’s what you want in this case ๐Ÿ™‚

    Login or Signup to reply.
  2. The problem is with the defaultValue props.

    defaultValue vs value:

    • defaultValue: Sets the initial value of an uncontrolled component. It does not update after the initial render, so subsequent changes to the state wonโ€™t affect the input value.
    • value: Controls the input field directly. The input will re-render and reflect any changes in the state immediately.

    I believe that it will work if you change to

    <input value={project.name} onChange={nameChange} />
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search