ウェブサイト検索

reactjs - Reactフック - 変更された状態がすぐに反映されない


私はクラスをReactのフックを使ったステートレスコンポーネントにリファクタリングしようとしています。

コンポーネント自体は非常にシンプルで、どこで間違えているのか分かりません。ほとんどReactのドキュメントからコピー&ペーストしただけです。

コンポーネントは、ユーザーがボタンをクリックしたときにポップアップを表示しています(ボタンはプロップスを通じて私のコンポーネントに渡されます)。私はtypescriptを使用しています。

hooks バージョンで私が望むことを実行できない行にコメントを付けました

こちらが私のオリジナルのクラスです。

export interface NodeMenuProps extends PropsNodeButton {
  title?: string
  content?: JSX.Element
  button?: JSX.Element
}
export interface NodeMenuState {
  visible: boolean
}
export class NodeMenu extends React.Component<NodeMenuProps, NodeMenuState> {
  state = {
    visible: false
  }

  hide = () => {
    this.setState({
      visible: false
    })
  }

  handleVisibleChange = (visible: boolean) => {
    this.setState({ visible })
  }

  render() {        
    return (
      <div className={this.props.className}>
        <div className={styles.requestNodeMenuIcon}>
          <Popover
            content={this.props.content}
            title={this.props.title}
            trigger="click"
            placement="bottom"
            visible={this.state.visible}
            onVisibleChange={this.handleVisibleChange}
          >
            {this.props.button}
          </Popover>
        </div>
      </div>
    )
  }
}

こちらがReactフック版です:

export interface NodeMenuProps extends PropsNodeButton {
  title?: string
  content?: JSX.Element
  button?: JSX.Element
}    
export const NodeMenu: React.SFC<NodeMenuProps> = props => {
  const [isVisible, setIsVisible] = useState(false)      

  const hide = () => {
    setIsVisible(false)
  }

  const handleVisibleChange = (visible: boolean) => {
    console.log(visible) // visible is `true` when user clicks. It works
    setIsVisible(visible) // This does not set isVisible to `true`.
    console.log(isVisible) // is always `false` despite `visible` being true.
  }      

  return (
    <div className={props.className}>
      <div className={styles.requestNodeMenuIcon}>
        <Popover
          content={props.content}              
          title={props.title}
          trigger="click"
          placement="bottom"
          visible={isVisible}
          onVisibleChange={handleVisibleChange}
        >
          {props.button}
        </Popover>
      </div>
    </div>
  )
}

回答 #1

setStateと同様に、フックを使った状態の更新も再レンダーと更新を必要とするため、変更はすぐには反映されません。しかし、handleVisibleChangeメソッドの外で状態をログしてみると、更新された状態が確認できます。

export const NodeMenu: React.SFC<NodeMenuProps> = props => {
  const [isVisible, setIsVisible] = useState(false)      

  const hide = () => {
    setIsVisible(false)
  }

  const handleVisibleChange = (visible: boolean) => {
    console.log(visible) // visible is `true` when user clicks. It works
    setIsVisible(visible) // This does not set isVisible to `true`.
  }      

  console.log({ isVisible });
  return (
    <div className={props.className}>
      <div className={styles.requestNodeMenuIcon}>
        <Popover
          content={props.content}              
          title={props.title}
          trigger="click"
          placement="bottom"
          visible={isVisible}
          onVisibleChange={handleVisibleChange}
        >
          {props.button}
        </Popover>
      </div>
    </div>
  )
}

状態が更新されたかどうかに基づいて行う必要のある操作は、次のように useEffect フックを使って行うことができます

useEffect(() => {
   // take action when isVisible Changed
}, [isVisible])

関連記事