【问题标题】:React - SetState handler MethodReact - SetState 处理程序方法
【发布时间】:2020-05-08 10:28:36
【问题描述】:

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

这是我在两个选择组件中选择两个球队(主场客场)时的两种方法

  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 正确返回所选团队的名称,所以我希望看到 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】:

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

    如果您确实想要记录最终将要进入的正确值(因为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);
    }, []);
    

    【讨论】:

    • 谢谢,我不能使用 item.name,因为我想将 item.name 与主队区分开来,将 item.name 与客队区分开来
    • 不确定我是否理解这个问题。 item.name 内部的 selectHomeTeamStatselectAwayTeamStat 内部的不一样。无论如何,我会选择useEffectuseCallback
    【解决方案2】:

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

    【讨论】:

    • 我理解这个解决方案,但我在 UseEffect 中有这个错误 => 'value' is not defined no-undef
    • 应该是你在setSelectedHomeOption中使用的selectedHomeOption
    • setState 是异步的,意味着您传递的值将最终进入状态,但这不是代码记录先前值的原因。如果它是同步的,它仍然会记录以前的值。您在这里将两个完全不相关的想法联系在一起。
    • items 是一个空数组在这种情况下,当它处于使用效果时,我将重现一个代码框
    • 我接受这个答案,因为它更专注于我的情况。感谢您的时间和解释
    【解决方案3】:
    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's response 建议您可以使用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);
      }; 
    

    【讨论】:

      猜你喜欢
      • 2017-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 2020-02-07
      • 2016-06-13
      • 1970-01-01
      相关资源
      最近更新 更多