React - SetState 处理程序方法

React - SetState handler Method

我有一个组件存在以下情况:

当我select两支球队(主场客场)在两个select时,这是两种方法组件

  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请求为了区分和创建然后两个不同的状态

在那之后我意识到我实际上需要 [=34= 而不是字符串 '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 returns 团队名称正确 selected 所以我希望看到 selectedHomeNameselectedAwayName 状态,但在我的 console.log 我没看到

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

所以问题是,我做错了什么?我如何将 这些状态 传递给事件处理程序中的 getStats 以进行区分?

这是在记录之前的值,因为它是函数定义时范围内的值,在调用它之前。

如果您真的想记录最终要记录的正确值(因为 setState 是异步的)使其进入同一位置的状态,只需记录 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,这样这两个函数就不会在每次重新渲染时重新创建,因为它们不需要访问 selectedHomeNameselectedAwayName 再:

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

const selectAwayTeamStat = React.useCallback(({ target }) => {
  setSelectedAwayName(target.textContent);
}, []);
console.log('Home Team Name:', selectedHomeName);
getStats(leagueId, value, selectedHomeName);

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

将始终是 selectedHomeNameselectedAwayName 的先前值。 这是因为您设置了将在事件处理程序中的下一次渲染时更新的值。

您应该使用 item.name 传递给 getStats。您可以在渲染或其他地方使用 selectedHomeNameselectedAwayName,或者作为 建议您可以使用 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);
  }; 

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]);