DOM рендеринг не срабатывает после обновления хуков - PullRequest
0 голосов
/ 16 января 2020

Я пытаюсь удалить элемент (const removeItem) из списка, используя событие onClick в React. Государство управляется с помощью крючков. Я знаю, что мой способ удаления элемента еще не правильный (я ставлю имя на ноль), но это не проблема.

После того, как я установил для пользователя значение NULL (я обновляю пользователей объект), я ожидаю, что рендер произойдет (useEffect), но это не так. Если я переключаю компоненты и go возвращаюсь к этому, это работает, но при нажатии кнопки X ничего не происходит в представлении.

component Home:

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 ]

3 голосов
/ 16 января 2020

Просматривая ваш код, я заметил, что 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 для правильной работы этого сценария.

Я надеюсь, что это решит вашу проблему.

2 голосов
/ 16 января 2020

Вы совершаете Основной грех реагирующей вселенной. «Мутация» состояния.

  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

Recon Reciler проверяет, равны ли эти 2 объекта, и поскольку вы только что мутировали, и установите значение, которое оно регистрирует как один и тот же объект, и состояние не изменится. срабатывает. Следовательно, представление не будет повторно отображено, и useEffect не будет запущено.

...