Флажки в карте не обновляются при обновлении массива после рефакторинга для реагирования на хуки - PullRequest
0 голосов
/ 17 марта 2020

Я преобразовал компонент класса в компонент функции с помощью хуков. В настоящее время я пытаюсь выяснить, почему флажки на этой карте не обновляются с проверенным значением, несмотря на срабатывание обработчика onChange и обновление массива по мере необходимости. (OnSubmit также работает и корректно обновляет значение в БД.)

import {
  Container,
  Typography,
  Grid,
  Checkbox,
  FormControlLabel,
  Button
} from "@material-ui/core";
import Select from "react-select";
import localeSelect from "../services/localeSelect";
import {
  linkCharactersToGame,
  characterLinked,
  linkCharacters
} from "../data/locales";
import dbLocale from "../services/dbLocale";
import { LanguageContext } from "../contexts/LanguageContext";
import { UserContext } from "../contexts/UserContext";
import { GameContext } from "../contexts/GameContext";
import { CharacterContext } from "../contexts/CharacterContext";
import { Redirect } from "react-router-dom";

export default function LinkCharacter() {
  const { language } = useContext(LanguageContext);
  const { user } = useContext(UserContext);
  const { games, loading, error, success, connectCharacters } = useContext(
    GameContext
  );
  const { characters } = useContext(CharacterContext);
  const [game, setGame] = useState("");
  const [selectedCharacters, setSelectedCharacters] = useState([]);
  if (!user) {
    return <Redirect to="/" />;
  }
  return (
    <section className="link-character">
      <Container maxWidth="sm">
        <Typography variant="h5">
          {localeSelect(language, linkCharactersToGame)}
        </Typography>
        {error && (
          <p className="error">
            <span>Error:</span> {error}
          </p>
        )}
        {success && <p>{localeSelect(language, characterLinked)}</p>}
        <Select
          options={games.map(game => {
            return {
              label: dbLocale(language, game),
              value: game._id
            };
          })}
          onChange={e => {
            setGame(e.value);
            const selected = [];
            const index = games.findIndex(x => x._id === e.value);
            games[index].characters.forEach(character => {
              selected.push(character._id);
            });
            setSelectedCharacters(selected);
          }}
        />
      </Container>
      <Container maxWidth="md">
        {game !== "" && (
          <>
            <Grid container spacing={2}>
              {characters.map((character, index) => {
                return (
                  <Grid item key={index} md={3} sm={4} xs={6}>
                    <FormControlLabel
                      control={
                        <Checkbox
                          value={character._id}
                          onChange={e => {
                            const index = selectedCharacters.indexOf(
                              e.target.value
                            );
                            if (index === -1) {
                              selectedCharacters.push(e.target.value);
                            } else {
                              selectedCharacters.splice(index, 1);
                            }
                          }}
                          color="primary"
                          checked={
                            selectedCharacters.indexOf(character._id) !== -1
                          }
                        />
                      }
                      label={dbLocale(language, character)}
                    />
                  </Grid>
                );
              })}
            </Grid>
            <Button
              variant="contained"
              color="primary"
              onClick={e => {
                e.preventDefault();
                connectCharacters(game, selectedCharacters);
              }}
            >
              {localeSelect(language, linkCharacters)}
            </Button>
          </>
        )}
      </Container>
    </section>
  );
}

Я чувствую, что в Hooks что-то не хватает (или есть какая-то проблема с обработкой Hooks что-то вроде этого) , Я искал и расспрашивал, и никто другой так и не смог разобраться в этом вопросе.

1 Ответ

0 голосов
/ 17 марта 2020

state, возвращаемое [state, setState] = useState([]) - это то, что вы должны только читать . Если вы измените его, React не будет знать, что данные изменились и что они должны быть повторно обработаны. Когда вам нужно изменить данные, вы должны использовать setState, или в вашем случае setSelectedCharacters.

Кроме того, изменение данных по ссылке может привести к непредсказуемым результатам, если массив будет считан в другом месте, позже .

В дополнение к этому, если вы дадите setState то же значение, которое хук вернул вам в state, React полностью пропустит обновление. Это не проблема при использовании чисел или строк, но становится единым при использовании массивов, потому что ссылка (значение, которое React использует, чтобы определить, есть ли разница) может быть одинаковой, когда содержимое могло измениться. Таким образом, вы должны передать новый массив setState.

С учетом этого ваша функция onChange может выглядеть следующим образом:

onChange={e => {
  const index = selectedCharacters.indexOf(
    e.target.value
  );
  if (index === -1) {
    // creating a new array with [], so the original one stays intact
    setSelectedCharacters([...selectedCharacters, e.target.value]);
  } else {
    // Array.filter also creates new array
    setSelectedCharacters(selectedCharacters.filter((char, i) => i !== index));
  }
}}

Do c здесь https://en.reactjs.org/docs/hooks-reference.html#usestate

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...