Hi I am trying to make a basic todo list app in React using Typescript and I struggle with filter logic. There are three filter values 'All', 'Active' and 'Completed' which should be stored in the filter variable using useState. But I got following error:
Argument of type 'MouseEvent<HTMLInputElement, MouseEvent>' is not assignable to parameter of type '"All" | "Active" | "Completed" | ((val: "All" | "Active" | "Completed") => "All" | "Active" | "Completed")'
Problematic is onClick event with setFilter() function. I tried to set onClick's type to (e: React.MouseEvent<HTMLInputElement>)
, but it didn't work.
Form.tsx:
type Task = {
id: string
name: string
completed: boolean
}
const filterMap = {
All: () => true,
Active: (task: Task) => !task.completed,
Completed: (task: Task) => task.completed,
}
const filterOptions = Object.keys(filterMap)
export const Form = () => {
const [name, setName] = useState('')
const [tasks, setTasks] = useLocalStorage('tasks:list', [] as Task[])
const [filter, setFilter] = useLocalStorage<keyof typeof filterMap>('task:filter', 'All')
const itemsLeft = tasks.filter(task => task.completed === false).length
const handleDelete = (id: string) => setTasks(tasks.filter(task => task.id !== id))
const handleDeleteChecked = () => setTasks(tasks.filter(task => task.completed === false))
const handleCheck = (id: string) =>
setTasks(tasks.map(task => (task.id === id ? { ...task, completed: !task.completed } : task)))
const checkIfSelected = tasks.find(task => task.completed)
return (
<form
onSubmit={e => {
e.preventDefault()
const newTask = {
id: generateId(),
name,
completed: false,
}
setTasks([...tasks, newTask])
setName('')
}}
>
<input
css={s.input}
type='text'
placeholder='What needs to be done?'
autoFocus={true}
required={true}
maxLength={t.taskMaxLength}
value={name}
onChange={e => setName(e.target.value)}
/>
<hr css={s.divider} />
{tasks.filter(filterMap[filter]).map(task => (
<div key={task.id}>
<div css={s.taskContainer}>
<Checkbox
checked={task.completed}
onChange={() => {
handleCheck(task.id)
}}
/>
{task.completed ? <p css={s.strikethrough}>{task.name}</p> : <p>{task.name}</p>}
<button css={s.deleteTask} type='button' onClick={() => handleDelete(task.id)}>
</button>
</div>
<hr css={s.divider} />
</div>
))}
<div css={s.footer}>
<div>{itemsLeft !== 1 ? `${itemsLeft} items left` : '1 item left'}</div>
<div css={s.filterBtnWrapper}>
{filterOptions.map(option => (
<input
key={option}
value={option}
type='button'
onClick={option => {
setFilter(option) // ERROR HERE
}}
css={[
s.clearCompleted,
s.filterBtn,
filter === option ? s.filterBtnSelected : undefined,
]}
/>
))}
</div>
{checkIfSelected ? (
<button type='button' css={s.clearCompleted} onClick={handleDeleteChecked}>
Clear completed
</button>
) : null}
</div>
</form>
)
}
CodePudding user response:
You have to set:
e: React.ChangeEvent<HTMLInputElement>
not
e: React.ChangeEvent
CodePudding user response:
Few changes in your code should do the trick:
filterOptions are string[], so casted them to this.
const filterOptions = Object.keys(filterMap) as (keyof typeof filterMap)[];
Next: your option as a first param is overriding option from map to MouseEvent... instead of what it was before, so
onClick={() => {
setFilter(option); // ERROR WAS HERE PREVIOUSLY
}}
Sorry, i cant completely reproduce your code but for me - no errors shown from tsc.