Search code examples
javascriptreactjsreact-functional-component

react functional component state object not rendering in display on change


I am trying to make a react example project with a way to add a 'project', edit its contents, view, and delete it. Just to practice with setting/changing/saving data.

I'm debating if I should make this as a functional or class component, so currently I am trying with a functional component below:

https://codesandbox.io/s/quizzical-moore-5s77l0?file=/src/Projects.js:568-570

import React, { useState } from "react";

function Projects() {
  const [projects, setProjects] = useState({});

  function addProject() {
    console.log("addProject()");
    //create project
    let id = Date.now().toString().substr(8);
    let newProject = {
      name: `Upload-${id}`,
      files: {}
    };
    //update projects state
    let oldProjects = projects;
    projects[id] = newProject;
    setProjects(oldProjects);
    console.log("projects = ", projects);
  }
  return (
    <div>
      <h1>Projects.js (Functional Component)</h1>

      <button onClick={addProject}>Add Project</button>

      <div>{Object.keys(projects).length} projs</div>
    </div>
  );
}

export default Projects;

when I click add projects, I can see the project get added to the state, but my display counting how many projects there are never changes? is there additional code I need to make my dom render when the state changes? or am i using the functional component wrong?

enter image description here


Solution

  • Interesting question you have here. Your use of functional components isn't really flawed. It looks like you're getting caught on a reference issue.

    Your <div> with the project count isn't updating because React doesn't know it needs to update it. React doesn't know it needs to update it because the reference to your projects object isn't changing. If you're confused, just search through articles on object reference equality in JavaScript.

    Long story short, your issue is with this line:

    let oldProjects = projects;
    

    A quick fix would be to do this:

    let oldProjects = { ...projects };
    

    ...or this to save a few lines:

    setProjects(oldProjects => ({ ...oldProjects, [id]: newProject }));
    

    There are performance reasons why these are not flawless solutions, but if this is just to get the hang of things, I don' think it's a problem.