Почему простая инициализация useState не работает?

У меня есть этот код React:

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

function App() {
  const [players, setPlayers] = useState([]);

  // Get all Players
  const getAllPlayersUrl = "http://localhost:5087/api/GetAllPlayers";
  useEffect(() => {
    axios.get(getAllPlayersUrl).then((response) => {
      setPlayers(response.data);
    });
  }, []);

  const [playerCount, setPlayerCount] = useState(players.length);

  return (
    <div>
      <p>{`This is how many there are: ${playerCount}`}</p>
    </div>
  );
}

export default App;

Я хочу напечатать, сколько начальных игроков, используя переменную playerCount. Однако он говорит, что это ноль:

Вот сколько их: 0

Если я вместо этого напечатаю players.length, он выведет правильное число:

 <p>{`This is how many there are: ${players.length}`}</p>

Вот сколько их: 9

Даже если я удалю массив зависимостей, чтобы продолжить рендеринг, playerCount все равно не будет обновляться:

  useEffect(() => {
    axios.get(getAllPlayersUrl).then((response) => {
      setPlayers(response.data);
    });
  });

Интересно, почему useState не работает? Есть ли что-то, что мне не хватает в моем коде?

Вам нужно вызвать setPlayerCount, если вы хотите... setplayerCount

CollinD 15.10.2022 22:49

Значение, которое вы передаете в useState, актуально только в первый раз по умолчанию. Зачем вообще использовать playerCount? Просто получите доступ к player.length там, где вам это нужно.

jonrsharpe 15.10.2022 22:51

Я не думаю, что мне нужно вызывать setPlayerCount, так как я не меняю playerCount. Я просто получаю его начальный счет, который уже инициализирован player.length

HeyLameRobin 15.10.2022 22:51

@jonrsharpe, потому что я хочу изменить playerCount в будущем.

HeyLameRobin 15.10.2022 22:53

Независимо от длины списка? Тогда почему то, что происходит прямо сейчас, является проблемой?

jonrsharpe 15.10.2022 22:54

@jonrsharpe да, независимо от списка. Это проблема прямо сейчас, потому что она не работает, и я хотел бы знать, почему.

HeyLameRobin 15.10.2022 22:57

Сейчас он не зависит от списка: вы никогда его не обновляете, он никогда не меняется. Вы говорите, что это неправильно, и хотите, чтобы это была длина списка, так что... используйте длину списка. У вас не может быть и того, и другого.

jonrsharpe 15.10.2022 23:15

@jonrsharpe Мой главный вопрос был «почему это не работает?». Ответ ниже помог мне понять, что когда состояние было инициализировано, players было пустым, и я никогда не обновлял его, поэтому да, это всегда будет 0. Это действительно все, что я искал. Если мне действительно нужно просто получить размер массива, я мог бы просто это сделать. Но я работаю над кодом, в котором я отслеживаю «Выбранные элементы» в этом массиве, поэтому я не могу просто использовать размер массива, поскольку «Выбранные элементы» будут постоянно меняться. Итак, я столкнулся с этой проблемой, отсюда и вопрос «почему это не работает?»

HeyLameRobin 15.10.2022 23:27
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
3
8
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Здесь вы устанавливаете начальное значение playerCount при монтировании компонента:

const [playerCount, setPlayerCount] = useState(players.length);

И компонент монтируется только один раз - и в это время players является пустым массивом - поэтому playerCount становится 0, и, поскольку вы никогда не вызываете setPlayerCount, он всегда остается 0.

Хотя вы можете исправить это, вызвав setPlayerCount внутри вашего .then, лучшим подходом будет либо вычисление количества игроков из состояния players только при необходимости:

function App() {
  const [players, setPlayers] = useState([]);
  const getAllPlayersUrl = "http://localhost:5087/api/GetAllPlayers";
  useEffect(() => {
    axios.get(getAllPlayersUrl).then((response) => {
      setPlayers(response.data);
    });
  }, []);

  return (
    <div>
      <p>{`This is how many there are: ${players.length}`}</p>
    </div>
  );
}

Или, если очень надо, запомнить счётчик в зависимости от массива players (без создания дополнительного состояния).

function App() {
  const [players, setPlayers] = useState([]);
  const playerCount = useMemo(() => players.length, [players]);
  const getAllPlayersUrl = "http://localhost:5087/api/GetAllPlayers";
  useEffect(() => {
    axios.get(getAllPlayersUrl).then((response) => {
      setPlayers(response.data);
    });
  }, []);

  return (
    <div>
      <p>{`This is how many there are: ${playerCount}`}</p>
    </div>
  );
}

Спасибо, что нашли время объяснить! Хотя я не использовал useMemo, я нашел способ исправить это.

HeyLameRobin 15.10.2022 23:35

Другие вопросы по теме