【问题标题】:Why isn't my useState hook working in this React component?为什么我的 useState 钩子在这个 React 组件中不起作用?
【发布时间】:2020-10-18 10:14:30
【问题描述】:

我整天都被困在这个问题上,希望能得到一些帮助。提前致谢。

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

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

最后我意识到错误是因为它正在将 todos 作为对象返回,所以我尝试了这个:

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

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

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

这是之后的状态:

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

这是我的其余代码:

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

【问题讨论】:

  • 在您的“Toggle isComplete”按钮中,您没有传递任何要切换 IsComplete 的 Todo 项的引用。您是否尝试为所有项目切换所有 IsComplete?
  • 最终我想通过 ID 切换单个项目,但我试图让它先处理一个项目。

标签: javascript reactjs react-hooks react-state-management use-state


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    我最终使用的方法,以及我看到其他开发人员所做的方法,是先复制对象或状态,对其进行修改,然后使用修改后的状态设置新状态。

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

    看一个工作示例,点击下面的“Run code sn-p”

    // 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>

    【讨论】:

    • 谢谢!这很有教育意义,而且效果很好。
    猜你喜欢
    • 2020-09-06
    • 2022-07-06
    • 2022-01-06
    • 2021-03-10
    • 2023-01-04
    • 2023-03-19
    • 2020-11-03
    • 2021-04-10
    • 2023-04-07
    相关资源
    最近更新 更多