Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

onClick type in React

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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>
  )
}

>Solution :

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading