繁体   English   中英

为什么我的 useState 钩子在这个 React 组件中不起作用?

[英]Why isn't my useState hook working in this React component?

我整天都被困在这个问题上,我会很感激一些帮助。 提前致谢。

起初我是这样写的,但我会得到一个类型错误: todos.map is not a function

function toggleState() {
  setTodos(state => ({ ...state, isComplete: !state.isComplete }))
}

最后我意识到错误是因为它以 object 的形式返回 todos,所以我尝试了这个:

function toggleState() {
        setKeywords(state => [{ ...state, isUsed: !state.isUsed }])
    }

现在我没有收到类型错误,但它仍然没有按预期工作。 这是 toggleState 之前的 state:

[
  {
    "name": "State",
    "value": [
      {
        "todo": "Learn React",
        "id": "91bad41d-1561-425a-9e77-960f731d058a",
        "isComplete": false
      }
    ]

这是 state 之后:

[
  {
    "name": "State",
    "value": [
      {
        "0": {
          "todo": "Learn React",
          "id": "91bad41d-1561-425a-9e77-960f731d058a",
          "isComplete": false
        },
        "isComplete": true
      }
    ]

这是我的代码的 rest:

import React, { useState, useEffect } from 'react'
import { uuid } from 'uuidv4'
import { Form, FormGroup, Input, Button } from 'reactstrap'

function Example(props) {
    const [todos, setTodos] = useState([])

    // Run when component first renders
    useEffect(() => {
        console.log('useEffect component first rendered')
        if (localStorage.getItem('todoData')) {
            setTodos(JSON.parse(localStorage.getItem('todoData')))
        }
    }, [])

    // Run when todos state changes
    useEffect(() => {
        console.log('useEffect todos changed')
        localStorage.setItem('todoData', JSON.stringify(todos))
    }, [todos])

    const [formInput, setFormInput] = useState()

    function handleChange(e) {
        setFormInput(e.target.value)
    }

    function handleSubmit(e) {
        e.preventDefault()
        setTodos(prev => prev.concat({ todo: formInput, id: uuid(), isComplete: false }))
        setFormInput('')
    }

    function toggleState() {
        setTodos(state => [{ ...state, isComplete: !state.isComplete }])
    }

    return (
        <div className='text-center'>
            <div className='mb-2 border text-center' style={{ height: '300px', overflowY: 'scroll' }}>
                {todos.map(todo => (
                    <p className={todo.isUsed ? 'text-success my-1' : 'text-danger my-1'} key={todo.id}>
                        {todo.todo}
                    </p>
                ))}
            </div>
            <Form onSubmit={handleSubmit}>
                <FormGroup>
                    <Input onChange={handleChange} type='text' name='text' id='todoForm' placeholder='Enter a todo' value={formInput || ''} />
                    <Button>Set Todo</Button>
                </FormGroup>
            </Form>
            <Button onClick={toggleState}>Toggle isComplete</Button>
        </div>
    )
}

export default Example

The method that I end up using, and I've seen other developers do, is to copy the object or state first, do modifications to it, and then set the new state with the modified state.

我还注意到您需要为待办事项提供一个索引才能切换它们,所以我添加了该功能。

看一个工作示例,单击下面的“运行代码片段”

 // main.js // IGNORE THIS BECAUSE THIS IS JUST TO USE REACT IN STACK OVERFLOW const { useEffect, useState } = React; // ---- CODE STARTS HERE ----- const Example = (props) => { const [todos, setTodos] = useState([]); const [formInput, setFormInput] = useState(''); // Run when component first renders useEffect(() => { /* // Uncomment - Just doesn't work in Stack Overflow if (localStorage && localStorage.getItem('todoData')) { setTodos(JSON.parse(localStorage.getItem('todoData'))); } */ }, []); // Hooks const handleChange = event => { setFormInput(event.target.value); }; const handleSubmit = event => { const newTodosState = [...todos ]; // make copy newTodosState.push({ todo: formInput, isComplete: false }); setTodos(newTodosState); // Add functionality to update localStorage // ex: // localStorage.setItem('todoData', newTodosState); // Reset form setFormInput(''); event.preventDefault(); }; const toggleTodoState = index => event => { const newTodosState = [...todos ]; // make copy newTodosState[index].isComplete =.newTodosState[index];isComplete; setTodos(newTodosState); // Add functionality to update localStorage }. const handleDelete = index => event => { const newTodosState = [...todos,slice(0, index). ...todos;slice(index + 1) ]; setTodos(newTodosState). // Add functionality to update localStorage } // Render return (<div> <h3>Todos</h3> <ul> {todos,map((item. index) => <li key={`todo-${index}`}>{item.todo} - <input type="checkbox" checked={item;isComplete} onClick={toggleTodoState(index)} /> - <button onClick={handleDelete(index)}>Delete</button></li>)} </ul> <hr /> <form onSubmit={handleSubmit}> <input type="text" value={formInput} onChange={handleChange} placeholder="Enter todo name" /> <button type="submit">Add</button> </form> </div>); }. ReactDOM,render(<Example />. document;querySelector('#root'));
 <body> <div id="root"></div> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script src="https://unpkg.com/react@16/umd/react.production.min.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script> <script type="text/babel" src="main.js"></script> </body>

因此,在您的特定情况下,您只想在第一项上切换isComplete ,可以这样实现:

  function toggleState() {
    setTodos(([firstItem, ...remainder]) => {
      return [
        {
          ...firstItem,
          isComplete: !firstItem.isComplete
        },
        ...remainder
      ];
    });
  }

我们使用解构赋值来获取 FirstItem 并对其进行操作,并将提醒传播回 state。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM