Reputation: 155
I am trying to pass todos
(initial state) and addNewTodo
(methods) using React Context hook and typescript. I have tried multiple solutions but no success, still getting errors.
Partial
generics doesn't give issue on context component, but it gives me the error Cannot invoke an object which is possibly 'undefined'
while calling addNewTodo
in todo form component.
Similarly, undefined and empty objects {} also give different errors. Can't figure out how to fix it. If I pass any
then IntelliSense won't work.
Here is my code
import React, { useState, createContext, FC, useContext } from "react"
type Props = {
children: React.ReactNode,
}
interface TaskContextProps {
todos: Todo[],
addNewTodo: addNewTodo
}
const initialTodos: Array<Todo> = [
{ id: 1, text: 'buy some milk', completed: false },
{ id: 2, text: 'go to gym', completed: false }
]
export const TaskListContext = createContext<Partial<TaskContextProps>>({})
// export const TaskListContext = createContext<TaskContextProps>({})
// export const TaskListContext = createContext<TaskContextProps | undefined>(undefined)
const TaskListContextProvider: FC<Props> = ({ children }) => {
const [todos, setTodos] = useState(initialTodos)
const addNewTodo: addNewTodo = (newTodo) => {
setTodos([newTodo, ...todos])
}
return (
<TaskListContext.Provider value={{ todos, addNewTodo }}>
{children}
</TaskListContext.Provider>
)
}
import React, { useState, ChangeEvent, FormEvent, useContext } from 'react';
// import { useTaskList, TaskListContext } from '../context/TaskListContext';
import { TaskListContext } from '../context/TaskListContext';
const TodoForm = () => {
const [newTodo, setNewTodo] = useState('')
// const { addNewTodo } = useTaskList()
const { addNewTodo } = useContext(TaskListContext)
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setNewTodo(e.target.value)
}
const handleSubmit = (e: FormEvent<HTMLButtonElement>) => {
e.preventDefault()
const addTodo = { id: Math.random(), text: newTodo, completed: false }
if (newTodo.trim()) {
addNewTodo(addTodo)
}
else {
alert('Todo can\'t be empty')
}
setNewTodo('')
}
return (
<form>
<input placeholder='your todo...' value={newTodo} onChange={handleChange} />
<button onClick={handleSubmit}>Submit</button>
</form>
)
}
Your help will be appreciated.
Upvotes: 4
Views: 1628
Reputation: 9662
To prevent TypeScript from telling that properties of an object are undefined we need to define them (using Partial sets every property as possibly undefined, which we want to avoid).
This means that we need to pass some value for each property when defining the context:
export const TaskListContext = createContext<TaskContextProps>({
todos: [],
addNewTodo: () => {}
});
This context initial values will be replaced as soon as the Provider is initialized, so they will never be read from a Component.
This way todos
and addNewTodo
will always have a value and TypeScript won't complain.
Upvotes: 6