更新挂钩后DOM渲染未触发

时间:2020-01-16 09:56:26

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

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

在将用户设置为null(更新用户对象)之后,我希望发生渲染(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>;
}

2 个答案:

答案 0 :(得分:3)

在检查您的代码时,我注意到有2次您在不使用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,而无需让对此有所反应。在这两种情况下,您都必须用users更新setUsers,而不能直接使数组变异。

  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钩子即可使此方案正常工作。

我希望这可以解决您的问题。

答案 1 :(得分: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协调器检查两个对象是否相等,并且由于您刚刚进行了突变并设置了值,因此它注册为同一对象,并且不会触发状态更改。因此,视图将不会重新渲染,也不会触发useEffect。

相关问题