【问题标题】:DOM render not triggering after updating hooks更新挂钩后未触发 DOM 渲染
【发布时间】:2020-05-03 03:19:10
【问题描述】:

我正在尝试使用 React 中的 onClick 事件从列表中删除项目 (const removeItem)。 状态是用钩子管理的。我知道我删除项目的方式还不是正确的方式(我将名称设为空),但这不是问题。

在我将用户设置为空(我更新用户对象)后,我希望渲染发生(useEffect)但它没有。如果我切换组件并返回到这个组件,它可以工作,但是当单击 X 按钮时,视图中没有任何反应。

组件首页:

import React, { Suspense, useState, useEffect } from "react";

const Home = props => {
  const [users, setUsers] = useState(props.users);

  console.log(users);

  useEffect(() => {}, [users]);

  const addItem = e => {
    users.push(e);
    console.log(e);
    e.preventDefault();
  };

  const removeItem = item => {
    users.forEach(user => {
      if (user.id === item) {
        user.name = null;
      }
    });
    console.log(users);
    setUsers(users);

  };

  return (
    <div className="Home">
      <form className="form" id="addUserForm">
        <input
          type="text"
          className="input"
          id="addUser"
          placeholder="Add user"
        />
        <button className="button" onClick={addItem}>
          Add Item
        </button>
      </form>

      <ul>
        {users.map(item => {
          return (
            <>
              <li key={item.id}>{item.name + " " + item.count}</li>
              <button className="button" onClick={() => removeItem(item.id)}>
                X
              </button>
            </>
          );
        })}
      </ul>
    </div>
  );
};

export default Home;

我如何获得用户对象:

import React from "react";

const LotsOfUsers =[...Array(100).keys()].map((item, key) => item = {
    name : `User ${key}`,
    id: key,
    count: 0
})

export default LotsOfUsers;

主应用:

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  useRouteMatch,
  useParams
} from "react-router-dom";
import Home from "./Home"
import CTX from './store'
import LotsOfUsers from "./LotsOfUsers";

export default function App() {
  return (
    <CTX.Provider value={{}}>
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/topics">Topics</Link>
          </li>
        </ul>

        <Switch>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/topics">
            <Topics />
          </Route>
          <Route path="/">
            <Home users={LotsOfUsers} text="hello world"/>
          </Route>
        </Switch>
      </div>
    </Router>
    </CTX.Provider>
  );
}

function About() {
  return <h2>About</h2>;
}

function Topics() {
  let match = useRouteMatch();

  return (
    <div>
      <h2>Topics</h2>

      <ul>
        <li>
          <Link to={`${match.url}/components`}>Components</Link>
        </li>
        <li>
          <Link to={`${match.url}/props-v-state`}>
            Props v. State
          </Link>
        </li>
      </ul>

      {/* The Topics page has its own <Switch> with more routes
          that build on the /topics URL path. You can think of the
          2nd <Route> here as an "index" page for all topics, or
          the page that is shown when no topic is selected */}
      <Switch>
        <Route path={`${match.path}/:topicId`}>
          <Topic />
        </Route>
        <Route path={match.path}>
          <h3>Please select a topic.</h3>
        </Route>
      </Switch>
    </div>
  );
}

function Topic() {
  let { topicId } = useParams();
  return <h3>Requested topic ID: {topicId}</h3>;
}

【问题讨论】:

  • 试试useEffect(() =&gt; {}, users);
  • 你好! useEffect(() => {}, 用户);和 useEffect(() => {}, [users]);不工作其中任何一个。
  • 是的,您必须通过filter 或简单地创建users 的副本,将新的引用传递给setUsers 而不是旧的users,修改副本并将副本传递给setUsers

标签: javascript html reactjs react-router react-hooks


【解决方案1】:

查看您的代码,我注意到您两次更改 users 而不使用 setUsers

  const addItem = e => {
    users.push(e); // <--- here
    console.log(e);
    e.preventDefault();
  };

  const removeItem = item => {
    users.forEach(user => {
      if (user.id === item) {
        user.name = null; // <--- here
      }
    });
    console.log(users);
    setUsers(users);

  };

这样,您正在更新您的users,而不会让 react 知道它。在这两种情况下,您都必须使用setUsers 更新users,而不是直接改变数组。

  const addItem = e => {
    setUsers(users.concat(e)); // sidenote: if e is your event, then you might be looking for e.target.value here 
    console.log(e);
    e.preventDefault();
  };

  const removeItem = item => {
    setUsers(users.filter(user => user.id !== item));
  };

.concat().filter() 都使用您的users 数组,并根据您要应用的更改返回一个新数组,然后setUsers 使用它来更新您的users

所以,我在removeItem 上实际做的事情是:

  const removeItem = item => {
    const newUsers = users.filter(user => user.id !== item); // users remains untouched
    setUsers(newUsers);
  };

此外,您不需要 useEffect 钩子即可使此方案正常工作。

我希望这能解决你的问题。

【讨论】:

    【解决方案2】:

    你正在犯下反应宇宙的根本罪。 “突变” 状态。

      const removeItem = item => {
        users.forEach(user => {
          if (user.id === item) {
            user.name = null; // <---  HERE
          }
        });
        console.log(users);
        setUsers(users);
    
      };
    

    查看此代码框以获取此问题的工作演示。 https://codesandbox.io/s/jovial-panini-unql4

    react reconciler 检查两个对象是否相等,因为您刚刚变异并设置了它注册为同一个对象的值,因此不会触发状态更改。因此不会重新渲染视图,也不会触发 useEffect。

    【讨论】: