我的状态没有使用 React Hooks 更新

Posted

技术标签:

【中文标题】我的状态没有使用 React Hooks 更新【英文标题】:My state is not updating using React Hooks 【发布时间】:2020-11-25 09:19:22 【问题描述】:

我有一个在更改时触发的事件。此事件检查哪个过滤器已更改,然后相应地更新状态。

如果我不使用 react hooks,我会选择这样的 -

const target = e.target;
const value = e.type === "checkbox" ? target.checked : target.value;
const name = e.target.name;    
this.setState( [name]: value );

如您所见,我可以使用[name] 更新状态名称,但由于我使用的是反应钩子,因此无法使用上述方法。相反,我得到了这个 -

  const [type, setType] = useState("all");
  const [capacity, setCapacity] = useState(1);
  const [price, setPrice] = useState(0);
  const [minPrice, setMinPrice] = useState(0);
  const [maxPrice, setMaxPrice] = useState(0);
  const [minSize, setMinSize] = useState(0);
  const [maxSize, setMaxSize] = useState(0);

  const handleChange = (e) => 
    const target = e.target;
    const value = e.type === "checkbox" ? target.checked : target.value;
    const name = e.target.name;
    
    switch (name) 
      case "type":
        setType(value);
        break;
      case "capacity":
        setCapacity(value);
        break;
      case "price":
        setPrice(value);
        break;
      case "minPrice":
        setMinPrice(value);
        break;
      case "maxPrice":
        setMaxPrice(value);
        break;
      case "minSize":
        setMinSize(value);
        break;
      case "maxSize":
        setMaxSize(value);
        break;
      default:
        return;
    
  ;

您在上面看到的内容由于某种原因不起作用。当我触发过滤器时,它会进入我已经测试过的情况,但状态似乎没有更新。名称和值也已记录,它们也很好。我不确定是什么原因造成的,而且我只是编程新手,因此我们将不胜感激!

*** 重申一下值和名称都有效,switch 语句也是如此。只是状态没有更新***

【问题讨论】:

你能粘贴完整的代码或显示问题的codeandbox小项目吗? 你如何验证/确认状态没有被更新? 能否请您粘贴完整的代码,并告诉我们您如何验证它是否正在更新状态。 我已经记录了它,但是因为我知道它不是即时的,所以我还设置了超时以在几秒钟后检查状态。这是完整的代码文件:gist.github.com/MelissaAstbury/cf9a60096a67a3361b69aadfdb4c34b8 ...在第 105 行检查了@Apostolos 只需使用生命周期方法在更新时记录状态,即componentDidUpdate 用于基于类的组件,useEffect 用于功能组件。你也可以使用 react-dev-tools 浏览器扩展来做一些状态监听。我怀疑您已将“当前”状态包含在 setTimeout 回调中。请更新您的问题以包含您正在运行的实际代码,以便我们查看相同代码。 【参考方案1】:

欢迎来到 SO。您仍然可以编写与您使用 useState 提供的 this.setState 示例类似的代码。这是一个可快速运行的示例。

围绕您的项目的答案:

    创建一个使用 useContext 的组件并只加载您需要的值和 handleChange 方法
const Test = () => 
  const  type, capacity, handleChange  = React.useContext(RoomContext);

  console.log(type, capacity);

  return (
      <div>
        <select name="type" onChange=handleChange>
            <option value="all">all</option>
            <option value="another">another</option>
            <option value="one">one</option>
        </select>
        <select name="capacity" onChange=handleChange>
          <option value="1">1</option>
          <option value="50">50</option>
          <option value="100">100</option>
        </select>
      </div>
  )
;
    将 Test 组件添加为 Room Provider 的子组件。 启动应用程序,您应该会看到它会正常更新

工作可运行示例:

const target = e.target;
const value = e.type === "checkbox" ? target.checked : target.value;
const name = e.target.name;    
this.setState( [name]: value );

const rootEl = document.getElementById('root');

const App = () => 
  const [form, setForm] = React.useState(
    name: '',
    checkbox: false,
  );
  
  const handleChange = (e) => 
    const target = e.target;
    const value = e.type === "checkbox" ? target.checked : target.value;
    const name = e.target.name;
    
    setForm(prevState => ( ...prevState, [name]: value ));
  
  
  return (
    <div>
      <label>Name</label>
      <input type="text" name="name" value=form.name onChange=handleChange />
      <label>Checkbox</label>
      <input type="checkbox" name="checkbox" checked=form.checkbox onChange=handleChange />
      <pre>JSON.stringify(form)</pre>
    </div>
  )


ReactDOM.render(<App />, rootEl);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

这是一个包含多个 useState 的代码的运行示例。

const rootEl = document.getElementById('root');

const App = () => 
  const [name, setName] = React.useState('');
  const [checkbox, setCheckbox] = React.useState(false);

  const handleChange = (e) => 
    const target = e.target;
    const value = e.type === "checkbox" ? target.checked : target.value;
    const name = e.target.name;
    
    switch (name) 
      case "name":
        setName(value);
        break;
      case "checkbox":
        setCheckbox(value);
        break;
      default:
        return;
    
  ;
  
  return (
    <div>
      <label>Name</label>
      <input type="text" name="name" value=name onChange=handleChange />
      <label>Checkbox</label>
      <input type="checkbox" name="checkbox" checked=checkbox onChange=handleChange />
      <pre>JSON.stringify( name, checkbox )</pre>
    </div>
  )


ReactDOM.render(<App />, rootEl);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

【讨论】:

如果你有一个 useState 那很好,但如果你有多个,如代码 sn-p 所示? @Andrew 添加了使用多个 useState 的代码的工作示例。什么都不应该被破坏,所以它应该可以工作。 谢谢!这和我现在的一样,但它似乎不起作用?见链接:gist.github.com/MelissaAstbury/cf9a60096a67a3361b69aadfdb4c34b8 你想和我分享这个项目,这样我就可以给你一个明确的答案,而不仅仅是一个单一的文件? @Win 我的 gitHub 项目是:github.com/MelissaAstbury/beach-resort ...谢谢你的帮助。文件名是 RoomContext.js【参考方案2】:

我的问题的解决方法是它没有重新渲染,所以它是整个时间的一个循环。

在 useEffect 中添加状态作为依赖项可以解决问题!我希望这可以帮助遇到类似问题的其他人,以某种方式帮助社区会很棒。

import React,  createContext, useState, useEffect  from "react";

import items from "../data";

export const RoomContext = createContext();

const RoomProvider = (props) => 
  const [rooms, setRooms] = useState([]);
  const [sortedRooms, setSortedRooms] = useState([]);
  const [featuredRooms, setFeaturedRooms] = useState([]);
  const [loading, setLoading] = useState(true);
  const [type, setType] = useState("all");
  const [capacity, setCapacity] = useState(1);
  const [price, setPrice] = useState(0);
  const [minPrice, setMinPrice] = useState(0);
  const [maxPrice, setMaxPrice] = useState(0);
  const [minSize, setMinSize] = useState(0);
  const [maxSize, setMaxSize] = useState(0);
  const [breakfast, setBreakfast] = useState(false);
  const [pets, setPets] = useState(false);

  //call the formatted data
  useEffect(() => 
    //set rooms
    const rooms = formatData(items);
    //set the featured rooms
    const featuredRooms = rooms.filter((room) => room.featured === true);
    //always show the max price as default
    const maxPrice = Math.max(...rooms.map((item) => item.price));
    //always show the max price as default
    const maxSize = Math.max(...rooms.map((item) => item.size));

    setRooms(rooms);
    setSortedRooms(rooms);
    setFeaturedRooms(featuredRooms);
    setLoading(false);
    setMaxPrice(maxPrice);
    setMaxSize(maxSize);
    setPrice(maxPrice);
  , [
    type,
    capacity,
    price,
    minPrice,
    maxPrice,
    minSize,
    maxSize,
    breakfast,
    pets,
  ]);

  //format data is getting all the required fields we want to pull from the originl data
  const formatData = (items) => 
    let tempItems = items.map((item) => 
      const id = item.sys.id;
      const images = item.fields.images.map((image) => image.fields.file.url);

      const room =  ...item.fields, images: images, id: id ;
      return room;
    );
    return tempItems;
  ;

  const getRoom = (slug) => 
    let tempRooms = [...rooms];
    const room = tempRooms.find((room) => room.slug === slug);
    return room;
  ;

  const handleChange = (e) => 
    const target = e.target;
    const value = e.type === "checkbox" ? target.checked : target.value;
    const name = e.target.name;

    switch (name) 
      case "type":
        setType(value);
        break;
      case "capacity":
        setCapacity(value);
        break;
      case "price":
        setPrice(value);
        break;
      case "minPrice":
        setMinPrice(value);
        break;
      case "maxPrice":
        setMaxPrice(value);
        break;
      case "minSize":
        setMinSize(value);
        break;
      case "maxSize":
        setMaxSize(value);
        break;
      case "breakfast":
        setBreakfast(value);
        break;
      case "pets":
        setPets(value);
        break;
      default:
        return;
    
    console.log(name, value);
  ;

  const filteredRooms = () => 
    let initialRooms = [...rooms];

    if (type !== "all") 
      initialRooms = initialRooms.filter((room) => room.type === type);
    
    console.log("room type is", type);
    setSortedRooms(initialRooms);
  ;

  return (
    <RoomContext.Provider
      value=
        rooms,
        featuredRooms,
        getRoom,
        loading,
        sortedRooms,
        type,
        capacity,
        price,
        minPrice,
        maxPrice,
        minSize,
        maxSize,
        breakfast,
        pets,
        handleChange,
        filteredRooms,
      
    >
      props.children
    </RoomContext.Provider>
  );
;

export default RoomProvider;

【讨论】:

很高兴您找到了答案!一切顺利,并尝试接受答案,以便其他有类似问题的人可以解决。【参考方案3】:

你导入 useState 了吗?

import React, useState from 'react';

【讨论】:

这不是答案。请写评论 我在第一篇文章中尝试过,但由于我只有 1 个声望,我不允许这样做(至少 50 个声望)。 我认为你应该写 e.target.value ''const value = e.type === "checkbox" ? e.target.checked : e.target.value;

以上是关于我的状态没有使用 React Hooks 更新的主要内容,如果未能解决你的问题,请参考以下文章

React:使用 Hooks 为深度嵌套对象设置状态

React Hooks:使用useState更新状态不会立即更新状态[重复]

useSelector 函数没有更新调度函数后的状态 -react hooks

使用 React Hooks 更新状态数组的所有对象元素

React Hooks - 将状态设置为初始状态

React hooks:如何检测特定状态变量何时更新