Search code examples
javascriptreactjsreact-hooksreact-context

React_Id replacing within useContext


I've created Context.Provider component which has in it's context state and action function:

//AppProvider.tsx//
import { createContext, useContext, useState } from "react";

export const AppContext = createContext<IAppContext>({
  state: {
    taskList: []
  },
  actions: {
    addTask: (_title: ITask["title"]) => { }
  },
});

export const AppProvider = ({ children }: { children: React.ReactNode }) => {
  const [taskList, setTaskList] = useState<IAppContext['state']['taskList']>([]);
  
  const addTask = (title: ITask["title"]) => {
    let id = `task_${Math.round(Math.random() * 1000000)}`;
    setTaskList([...taskList, { id, title, steps: [] }]);
  };
  };

  const value = {
    state: {
      taskList: taskList
    },
    actions: {
      addTask
    },
  };

  return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
};

export const useData = () => {
  return useContext(AppContext)
}

In my NewTask component I've got access to the context and call addTask function:

NewTask.tsx
import { useRef } from "react";
import { useData } from "../../../AppProvider";

export default function NewTask() {
  const inputRef = useRef<Nullable<HTMLInputElement>>(null);
  const { actions: { addTask } } = useData();

  const handleAddTask = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    if (inputRef.current) {
      let inputText = inputRef.current.value
      if (inputText.length > 0) {
        addTask(inputText)
        inputRef.current.value = ""
      }
    }
  }

  return <div className={styles.newTask}>
    <form className={styles.form} onSubmit={handleAddTask}>
      <input type="text" placeholder="Add new task..."
        name="title" className={styles.newTask} ref={inputRef} />
      <button className={styles.iconButton} >
        <img src="icons/plus.png" alt="Add task" />
      </button>
    </form>
  </div>;
}

As an output for taskList I've all ids replaced with the last id value: state

How to fix this bug?

I've tried to change addTask function to this one, but with no luck:

const addTask = (title: ITask["title"]) => {
  let id = `task_${Math.round(Math.random() * 1000000)}`;
  setTaskList((tasks) =>
    tasks.concat([{ id, title, steps: [] }])
  );
};

Reprodused example on codesandbox


Solution

  • Your problem is in your Task component. Your .filter() condition is written incorrectly:

    const [task] = state.taskList.filter((task) => task.id = id);
    

    Here task.id = id is not performing a comparison, it's assigning task.id to id, hence why all your task objets within your state end up with the same id. You could change this to use == or ===, but you don't need to do an additional search on your array here. Instead, pass the task from your TaskList component into Task:

    return (
        <ol className="taskList">
          {taskList.map((task) => (
            <Task key={task.id} task={task} />
          ))}
        </ol>
    );
    

    Then your Task component can use task directly, no more filtering required (note the fragement <></> isn't required here, but perhaps your actual code has multiple JSX elements you want to return):

    export default function Task({ task }: {task: ITask}) {
      return (
        <>
          <p className="title">{`Task_Title- ${task.title}`}</p>;
        </>
      );
    }