React - Метод обработчика SetState - PullRequest
2 голосов
/ 08 мая 2020

У меня есть компонент со следующей ситуацией:

Это два метода, когда я выбираю две команды ( Home , Away ) в двух выбранных компонентах

  const selectHomeTeamStat = evt => {
    const { value } = evt.target;
    setSelectedHomeOption(value);
    getStats(leagueId, value, 'home');
  };

  const selectAwayTeamStat = evt => {
    const { value } = evt.target;
    setSelectedAwayOption(value);
    getStats(leagueId, value, 'away');
  };

Как видите, я могу передать этот строковый параметр « Home » и « Away » в запрос getStats, чтобы различать и создавать затем два разных состояния

После этого я понял, что вместо строк ' Home ' и ' Away ' мне действительно нужно имя состояния команды для передачи в качестве параметра в запрос getStats

Таким образом, это изменено на

  const [selectedHomeName, setSelectedHomeName] = useState("");
  const [selectedAwayName, setSelectedAwayName] = useState("");

  const selectHomeTeamStat = evt => {
    const { value } = evt.target;
    const item = items.find(item => item.team_id == value);
    setSelectedHomeOption(value);
    setSelectedHomeName(item.name);
    console.log('Home Team Name:', selectedHomeName);
    getStats(leagueId, value, selectedHomeName);
  };

  const selectAwayTeamStat = evt => {
    const { value } = evt.target;
    const item = items.find(item => item.team_id == value);
    setSelectedAwayOption(value);
    setSelectedAwayName(item.name);
    console.log('Away Team name:', selectedAwayName);
    getStats(leagueId, value, selectedAwayName);
  };

item.name возвращает правильно имя выбранной команды, поэтому я ожидаю увидеть selectedHomeName и selectedAwayName тоже состояния, но в моем console.log я не вижу его

console.log('Home Team Name:', selectedHomeName); => Team Name:
console.log('Away Team Name:', selectedAwayName); => Away Name:

Итак, вопрос в том, что я делаю не так? как я могу передать эти состояния в getStats внутри обработчика событий, чтобы различать?

Ответы [ 3 ]

6 голосов
/ 08 мая 2020

Это регистрирует предыдущее значение, так как оно было в области видимости, когда функция была определена, перед ее вызовом.

Если вы действительно хотите записать правильное значение, которое в конечном итоге будет (как setState is asyn c) переведите его в состояние в том же месте, просто запишите item.name.

В качестве альтернативы, если вы хотите регистрировать значение только после того, как оно уже находится в состоянии, вы должны использовать useEffect, передавая ему значения, на которые вы хотите реагировать, как deps:

// We want React to call this function every time `selectedAwayName` changes:
useEffect(() => {
  console.log('Away Team name:', selectedAwayName);
}, [selectedAwayName]);

Здесь вы можете увидеть, что происходит и как useEffect исправляет это:

const App = () => {
  const [selectedHomeName, setSelectedHomeName] = React.useState('');
  const [selectedAwayName, setSelectedAwayName] = React.useState('');

  // When the component renders for the first time, this function is created,
  // and the `selectedHomeName` value that will log when called is the one
  // currently in scope, that is, an empty string.
  
  // When the component re-renders, a new function is created again with the
  // value currently in scope, which is the one we set previously. When changed
  // again, it will log the previous value, not the new one:
  
  const selectHomeTeamStat = ({ target }) => {
    setSelectedHomeName(target.textContent);
    console.log('PREVIOUS Home Team =', selectedHomeName);
  };

  const selectAwayTeamStat = ({ target }) => {
    setSelectedAwayName(target.textContent);
    console.log('PREVIOUS Away Team =', selectedAwayName);
  };
  
  // We are telling React to call this function every time `selectedHomeName` or
  // `selectedAwayName` change:
  React.useEffect(() => {
    console.log(`CURRENT TEAMS = ${ selectedHomeName } / ${ selectedAwayName }`);
  }, [selectedHomeName, selectedAwayName])
  
  return (<React.Fragment>
    <nav className="buttons">
      <button onClick={ selectHomeTeamStat }>Home Team 1</button>
      <button onClick={ selectHomeTeamStat }>Home Team 2</button>
      <button onClick={ selectAwayTeamStat }>Away Team 1</button>
      <button onClick={ selectAwayTeamStat }>Away Team 2</button>    
    </nav>
    
    <div>{ selectedHomeName } / { selectedAwayName }</div>
  </React.Fragment>);
}

ReactDOM.render(<App />, document.querySelector('#app'));
body,
button {
  font-family: monospace;
}

body, p {
  margin: 0;
}

#app {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-height: 100vh;
}

.buttons {
  display: flex;
  margin: 32px 0;
}

button {
  margin: 0 4px;
  padding: 8px;
  border: 2px solid black;
  background: transparent;
  cursor: pointer;
  border-radius: 2px;
}

.as-console-wrapper {
  max-height: 45px !important;
}
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>

С учетом этих изменений теперь вы можете использовать useCallback, чтобы эти две функции не создавались повторно. при каждом повторном рендеринге, поскольку им больше не нужен доступ к selectedHomeName или selectedAwayName:

const selectHomeTeamStat = React.useCallback(({ target }) => {
  setSelectedHomeName(target.textContent);
}, []);

const selectAwayTeamStat = React.useCallback(({ target }) => {
  setSelectedAwayName(target.textContent);
}, []);
1 голос
/ 08 мая 2020

setState - асинхронный. Вы регистрируете состояние в текущей фазе рендеринга, обновленное значение будет доступно только при следующем рендере.

Чтобы исправить это, передайте текущее значение item.name или используйте useEffect

const selectHomeTeamStat = (evt) => {
    const { value } = evt.target;
    setSelectedHomeOption(value);
}

useEffect(() => {
  const item = items.find((item) => item.team_id == selectedHomeOption);
  getStats(leagueId, selectedHomeOption, item.name);
}, [leagueId, items, selectedHomeOption]);
1 голос
/ 08 мая 2020
console.log('Home Team Name:', selectedHomeName);
getStats(leagueId, value, selectedHomeName);

и

console.log('Away Team name:', selectedAwayName);
getStats(leagueId, value, selectedAwayName);

Всегда будет предыдущее значение selectedHomeName и selectedAwayName. Это потому, что вы устанавливаете значение, которое будет обновлено при следующем рендеринге в ваших обработчиках событий.

Вы должны использовать item.name для перехода к getStats. Вы можете использовать selectedHomeName и selectedAwayName в рендере или где-то еще или как ответ Данцингера , предлагающий вам прослушивать изменения на них с помощью useEffect

const selectHomeTeamStat = evt => {
    const { value } = evt.target;
    const item = items.find(item => item.team_id == value);
    setSelectedHomeOption(value);
    setSelectedHomeName(item.name);
    console.log('Home Team Name:', item.name);
    getStats(leagueId, value, item.name);
  };

  const selectAwayTeamStat = evt => {
    const { value } = evt.target;
    const item = items.find(item => item.team_id == value);
    setSelectedAwayOption(value);
    setSelectedAwayName(item.name);
    console.log('Away Team name:', item.name);
    getStats(leagueId, value, item.name);
  }; 
...