React: バグ:onKeyDownの無関係な状態の更新により、onChangeがブロックされ、制御されたコンポーネントが破損します

作成日 2020年07月09日  ·  3コメント  ·  ソース: facebook/react

こんにちは、私は入力制御モードを使用し、 onKeyDownおよびonChangeイベントをサブスクライブします。 ユーザーがスペースに入るとエフェクトがトリガーされます。 ただし、スペースは画面に表示されません。 これはReactのバグですか、それとも私が使用している方法に何か問題がありますか? ただし、 onKeyUpサブスクライブするのは正常です。 これは混乱しています。

Reactバージョン:16.13.1以前

再現する手順

  1. スペースに入る
  2. 入力要素が画面にスペースを表示しない

コード例へのリンク:

import React, { useState,useEffect } from 'react'

const App = () => {
  const [count, setCount] = useState(0)
  const [value, setValue] = useState('')
  const [code, setCode] = useState('add')

  useEffect(() => {
    if (code === 'add') {
      setCount(c => c + 1)
    } else {
      setCount(c => c - 1)
    }
  }, [code, setCount])

  const keyDown = e => {
    if (e.keyCode === 32) {
      console.log('space~')
      if (code === 'add') {
        setCode('minus')
      } else {
        setCode('add')
      }
    }
  }

  const handleChange = e => {
    setValue(e.target.value)
  }

  return (
    <div>
      <div>{count}</div>
      <br />
      <input onKeyDown={keyDown} value={value} onChange={handleChange} />
    </div>
  )
}

export default App

現在の動作

入力要素がスペースを受け取ることができません

期待される動作

入力要素はスペースを受け取ることができます

DOM Unconfirmed Bug

全てのコメント3件

onKeyDownハンドラーの状態更新がイベントを中断し、 onChangeハンドラーが呼び出されないように見えます。 制御されたコンポーネントは、指示した内容(例ではvalue状態)のみを表示するため、スペース文字は表示されません。

これは、これら2つのイベントが相互作用する方法で私にはバグのように見えます。

イベントシステムの専門知識を提供してくれたcc @ trueadm @gaearon

こんにちは、私はここで新しく、Reactアーキテクチャを学ぼうとしています。

@bvaughnこれが私が問題について見つけたものです。

onKeyDownイベントは、入力が変更される前にトリガーされるのではなく、 onChangeブロックしません。 基本的に、テキスト入力はonKeyUpれるため、 onKeyDownonChangeよりも早くなります。

この特定のケースでは、問題は、 keyDownで、 setCode呼び出されたときに、最初のdispatchAction _Fiber_に新しい文字がないmemoizedStateことです(明らかに)追加されるe 。 最後に、入力に新しい文字を追加します。 次に、 useEffect()は_同じキュー_内でdispatchAction再度トリガーします(キューかどうかはわかりません)が、更新された小道具については何も知りません。 その結果、 commitRoot場合、 memoizedStateと同じpendingPropsが復元されます。 つまり、文字(この例ではスペース)が追加された後、すぐに古い状態が新しい文字(スペース)なしで復元されるため、何も追加されていないように見えます。

_same queue_(?)を使用しない例として、この特定の例:

useEffect(() => {
    if (code === 'add') {
      setCount(c => c + 1)
    } else {
      setCount(c => c - 1)
    }
  }, [code, setCount])

次のように変更できます。

useEffect(() => {
    if (code === 'add') {
      setTimeout(() => setCount(c => c + 1), 0)
    } else {
      setTimeout(() => setCount(c => c - 1), 0)
    }
  }, [code, setCount])

そしてそれは動作します。

私はそれが理にかなっていることを願っています。

これと同じ問題が発生し、キーダウンハンドラーのsetTimeoutでsetState呼び出しをラップすると修正されました。

これがReactの内部で異なる動作をするはずなのか、それともこのエラーが開発者側のより大きな間違いや悪いパターンを示しているのか知りたいのですが。

このページは役に立ちましたか?
0 / 5 - 0 評価