Search code examples
reactjsreact-hooksuse-state

React not rendering list after the state is changed


I am creating a simple tracker which records all the activities done. It is my first project in react. I have created three state one for storing all the items(name of state is list), one for pending items(name of state is pending) , one for completed items(name of state is completed). The items have a button which when clicked marks it into done state and vice-versa. It is completely rendering items for main list. But for other two its not rendering. When I am checking with react developer tools, it is working fine, i.e. it is adding to pending list or completed list as it should. But it is not compiling them on screen. list is already filled with items. I have added all the code for just in case.

function Filters(props){
  const [completed, setCompleted] = useState([]);
  const [pending, setPending] = useState([]);
  const [state, setState] = useState("None");
  const [list,setList] = useState([]);

  function viewState(){
    setState("View-all");
  }

   //it is getting the clicked item id and marking it complete in main list
  function markComplete(id){
    list.map((items,index)=>{
      if(index===id){
        if(items.done===true)
          items.done = false;
        else{
          items.done=true;
        }
      }
    })
  }

//i am simply scanning the main list and the items which are pending will be added to this list. //this happens whenever the person click on pending button
 function pendingState(){
    setState("pending-all");
    setPending([]);
    list.map(items=>{
      if(items.done!==true){
        setPending(prev=>{
          return [...prev,items];
        })
      }
    })
  }

  function completedState(){
    setState("completed-all");
    setCompleted([]);
    list.map(items=>{
      if(items.done===true){
        setCompleted(prev=>{
          return [...prev,items];
        })
      }
    })
  }

 return (
    <div>
      <div className="input-section">
        <Note setList={setList} />
      </div>
      <button type="button" onClick={viewState} >View All</button>
      <button type="button" onClick={completedState}>Completed</button>
      <button type="button" onClick={pendingState}>Pending</button>
      <div>
        {
          (()=>{
            if(state==="View-all")
            {
              return (
                <div>
                  <h1>Working {completed}</h1>
                  {(list).map((items,index)=>
                    {
                    return (

                      <Excercise
                        key={index}
                        id={index}
                        title={items.name}
                        list={props.list}
                        setList={props.setList}
                        content={items.desp}
                        markComplete={markComplete}
                        />
                    )
                  })}
                </div>
              )
            }
            else if(state==="completed-all")
            {
              return (
                <div>
                {completed.map((items,index)=>{
                  <Excercise
                    key={index}
                    id={index}
                    title={items.name}
                    list={props.list}
                    setList={props.setList}
                    content={items.desp}
                    markComplete={markComplete}
                    />
                })}
                </div>
              )
            }
  })()
        }

      </div>


    </div>);
}



Kindly help. Thank you.

Hi @DREW The function code :

  function markComplete(id){
    setList(lists=>{
      lists.map(item=>{
      return item.id===id ?{...item,done: !item.done} : (item);})
      }
      )
  }

When I am using it instead of

 const markComplete = (id) => {
    setList((list) =>
      list.map((item) =>
        item.id === id
          ? {
              ...item,
              done: !item.done
            }
          : item
      )
    );
  };

it is showing, "Cannot read properties of undefined (reading 'filter')" arent the both same. If not, what am I doing wrong. Sorry for bugging so many times, I have just started with react.


Solution

  • I think you've overcomplicated things a bit. You only need one array to store the exercises in, the "pending" and "completed" states are easily derived from the list state and the state filter state value.

    Issues

    • markComplete callback is mutating the list state. When updating the list state not only is a new array reference necessary, but also new element object references are necessary for the elements that are being updated.
    • Uses poor boolean comparisons to set a boolean value. You can either toggle a boolean or set the value to the result of a boolean expression.
    • Use the viewState, pendingState, and completedState handlers to simply set the filter value, and then derive the computed state when rendering by adding an inline filter function.
    • Use the exercise id property as a React key and as the property used for toggling the completed (done) state.

    Solution

    function Filters(props) {
      const [state, setState] = useState("None");
      const [list, setList] = useState([
        ...
      ]);
    
      function viewState() {
        setState("View-all");
      }
    
      function pendingState() {
        setState("pending-all");
      }
    
      function completedState() {
        setState("completed-all");
      }
    
      const markComplete = (id) => {
        setList((list) =>
          list.map((item) =>
            item.id === id
              ? {
                  ...item,
                  done: !item.done
                }
              : item
          )
        );
      };
    
      return (
        <div>
          <div className="input-section">
            <Note setList={setList} />
          </div>
          <button type="button" onClick={viewState}>
            View All
          </button>
          <button type="button" onClick={completedState}>
            Completed
          </button>
          <button type="button" onClick={pendingState}>
            Pending
          </button>
          <div>
            {list
              .filter((item) => {
                if (state === "pending-all") {
                  return !item.done;
                } else if (state === "completed-all") {
                  return item.done;
                }
                return true;
              })
              .map((item) => (
                <Excercise
                  key={item.id}
                  id={item.id}
                  done={item.done}
                  title={item.name}
                  content={item.desp}
                  markComplete={markComplete}
                />
              ))}
          </div>
        </div>
      );
    }
    

    Edit react-not-rendering-list-after-the-state-is-changed