React - SetState处理方法

问题描述 投票:0回答:1

我有一个组件有以下情况。

当我选择两支队伍时,有两种方法(首页, 客场),分为两个部分

  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');
  };

正如你所看到的,我能够传递这个字符串参数''。首页'和'客场'至 getStats 请求,以区分并创建两个不同的状态。

此后,我意识到,而不是字符串''。首页'和'客场'我其实需要的 队名 作为参数传递给 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 正确地返回所选队伍的名称,所以我希望看到的是 selectedHomeNameselectedAwayName 但在我 console.log 我没有看到它

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

所以问题是,我到底做错了什么,如何才能通过 这些国家getStats 的事件处理程序内,以便区分?

javascript reactjs event-handling react-hooks setstate
1个回答
1
投票

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

6
投票

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

如果你真的想记录正确的值,那最终会(如 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/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/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);
}, []);

1
投票
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 在渲染或其他地方或作为 Danzinger的答复 建议你可以通过以下方式收听他们的变化 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);
  }; 
© www.soinside.com 2019 - 2024. All rights reserved.