0

I am trying to use React Context successfully but I a have had a lot of trouble with it. I can't even successfully pass anything one level to the provider's immediate children, as a result all I am getting at this stage is "x is undefined" errors in the console. I am using a separate class for the context an a custom hook to manage my state data.

App.js (where TodoProvider component warps around its children) -

import logo from './logo.svg';
import './App.css';
import React, {createContext, useContext} from "react"
import TodoItem from './Components/Other/TodoItem';
import TodoList from './Components/List/TodoList';
import TodoAdd from './Components/Forms/TodoAdd';
import CompletedTaskList from './Components/List/CompletedTaskList';
import useTodo from './libs/useTodo';
import {TodoContext, TodoProvider} from "./Contexts/TodoContext"

function App() {
  const {
    todoArray, setTodoArray,
    completedTaskArray,
    addCompletedItem,
    addTodoItem
  } = useContext(TodoContext);

  return (
    <TodoProvider
      value={
        todoArray, setTodoArray,
        completedTaskArray,
        addCompletedItem,
        addTodoItem
      }
    >
      <div className="App">
        <div className='card' id='mainCard'>
          <div className='card-header' id='mainCardHeader'><h4>Todo List</h4></div>
          <TodoList/>
          <TodoAdd
          />
          <CompletedTaskList
          />
        </div>
      </div>
    </TodoProvider>
    )
}

export default App;

TodoContext.js (My Context) -

import React, {createContext} from "react";
import useTodo from "../libs/useTodo";

const TodoContext = createContext();

const TodoProvider = ({children}) => {
    const {
        todoArray, setTodoArray,
        completedTaskArray,
        addCompletedItem,
        addTodoItem
      } = useTodo();
    return (
        <TodoContext.Provider
            value={
                todoArray, setTodoArray,
                completedTaskArray,
                addCompletedItem,
                addTodoItem
            } 
        >
            {children}
        </TodoContext.Provider>
    )
}

export {TodoContext, TodoProvider}

useTodo.js (My custom hook to manage state)

import React, {useState} from "react"

const useTodo = () => {
      
  const [todoArray, setTodoArray] = useState([{id: 1,todoTitle: "Code", todoDescription: "Practice React"},{id: 2,todoTitle: "Clean", todoDescription: "Wash dishes, wipe surfaces"}]);
  const [completedTaskArray, setCompletedTaskArray] = useState(["Wake up", "Make Bed"]);

  const [currentId, setCurrentId] = useState(3);

  const addTodoItem = (todoTitleInputItem, todoDescriptionInputItem) => {
    let todoTitle = todoTitleInputItem;
    let todoDescription = todoDescriptionInputItem;
    let id = currentId;
    setCurrentId(currentId+1)
    setTodoArray(todoArray => [...todoArray, {id,todoTitle, todoDescription}]);
  }

  const addCompletedItem = ({todoTitle}) => {
    setCompletedTaskArray(completedTaskArray => [...completedTaskArray, todoTitle]);
  }

  return {
      todoArray, setTodoArray,
      completedTaskArray, setCompletedTaskArray,
      addTodoItem,
      addCompletedItem
  }
}

export default useTodo;

CompletedTasklist(An example of my implementation of using a the context in one of it's children) -

import { useContext } from "react";
import {TodoContext, TodoProvider} from "../../Contexts/TodoContext"
const CompletedTaskList = () => {

    const {
        completedTaskArray
    } = useContext(TodoContext);


    return (
        <div className="card todo-item">
            <div className="card-header">
                <h3> Completed Task</h3>
            </div>
            <div className="card-body">
                <ul className="list-group ">
                    {completedTaskArray.map((item,index) => {
                        return <li className="list-group-item list-group-item-success" key={index}>{item}</li>
                    })}
                </ul>
            </div>
        </div>
    )
}

export default CompletedTaskList;   

I've been trying to resolve this for a while now and cannot wrap my mind around it.

3
  • 2
    stackblitz.com/edit/react-wq6g57 have added comments should help in understanding it Commented Jan 14, 2022 at 16:27
  • @N.K. Thanks a ton for providing a clear comments explaining what I was doing wrong. Commented Jan 14, 2022 at 16:38
  • 1
    @N.K. Why not answer the question on the site here using a stack snippet? That link can disappear or change over time, making the answer useless. Commented Jan 14, 2022 at 17:19

1 Answer 1

1

App.js

import React, { createContext, useContext } from 'react';
import CompletedTaskList from './comp';
import { TodoProvider } from './context';

function App() {
  // you dont need useTodo, or TodoContext here
  return (
    <TodoProvider>
      {/** todo Provider is a wrapper, you dont need to pass value as prop again, you are already doing it */}
      <div className="App">
        <div className="card" id="mainCard">
          <div className="card-header" id="mainCardHeader">
            <h4>Todo List</h4>
          </div>

          <CompletedTaskList />
        </div>
      </div>
    </TodoProvider>
  );
}

export default App;

Context

import React, { createContext } from 'react';
import useTodo from './useTodo';

// Define default values of your context data.
// otherwise everything would be undefined and you need to handle it everywhere
// you are using context
const TodoContext = createContext({
  todoArray: [],
  setTodoArray: () => {},
  completedTaskArray: [],
  addCompletedItem: () => {},
  addTodoItem: () => {},
});

const TodoProvider = ({ children }) => {
  const {
    todoArray,
    setTodoArray,
    completedTaskArray,
    addCompletedItem,
    addTodoItem,
  } = useTodo();
  return (
    <TodoContext.Provider
      value={{
        // <--- you were passing value incorrectly here, it should be an object
        // you passed it as (......) instead of {...}
        // curly braces not paranthesis
        todoArray,
        setTodoArray,
        completedTaskArray,
        addCompletedItem,
        addTodoItem,
      }}
    >
      {children}
    </TodoContext.Provider>
  );
};

export { TodoContext, TodoProvider };

Repasting the answer from the link i shared above.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.