【问题标题】:React 16.4 - manual form input fill along with its updates from getDerivedStateFromProps?React 16.4 - 手动表单输入填充以及来自 getDerivedStateFromProps 的更新?
【发布时间】:2019-12-01 04:41:07
【问题描述】:

我在 React 16.4 更新后遇到了一个问题,我们对 getDerivedStateFromProps 逻辑进行了一些重大更改。现在它会在 incomingown 组件的 props 上触发每个组件更新。

所以,我已经阅读了文档和手册,但仍然无法弄清楚表单输入字段应该基于传入道具 (controlled component) 并且同时能够通过以下方式进行修改的情况用户自己的输入?

我也试过这篇文章,但它只涵盖一次性更新的案例,而不是手动输入案例:Why getDerivedStateFromProps is called after setState?

这是我要重现的小代码:

import PropTypes from 'prop-types'
import React from 'react'

export class NameEditor extends React.Component {
  static propTypes = {
    currentLevel: PropTypes.number
  }

  static defaultProps = {
    currentLevel: 0
  }

  constructor(props) {
    super(props)

    this.state = {
      currentLevel: 0
    }
  }

  static getDerivedStateFromProps(nextProps) {
    return {
      currentLevel: nextProps.currentLevel
    }
  }

  _handleInputChange = e => {
    this.setState({
      currentLevel: e.target.value
    })
  }

  render() {
    const { currentLevel } = this.state

    return (
        <input
          placeholder={0}
          value={currentLevel}
          onChange={this._handleInputChange}
        />
    )
  }
}

export default NameEditor

【问题讨论】:

    标签: javascript reactjs getderivedstatefromprops


    【解决方案1】:

    解决方案 #1(带钥匙并重新安装):

    您可能需要根据传入的 prop:currentLevel 为其提供一个密钥,从而在每个外部 props 更新时重新挂载当前组件。它看起来像:

    class Wrapper ... {
    ...
    
      render() {
        const { currentLevel } = this.props;
    
        return (
         <NameEditor key={currentLevel} {...currentLevel} />
        )
      }
    }
    
    export default Wrapper
    

    ...并在您的组件上进行一些额外的更改,以阻止派生的道具替换通过告诉它 - 它是否是第一次渲染(因为我们计划仅从内部控制它的state,仅通过重新安装从外部控制它,当它真的如此):

    import PropTypes from 'prop-types'
    import React from 'react'
    
    export class NameEditor extends React.Component {
      static propTypes = {
        currentLevel: PropTypes.number
      }
    
      static defaultProps = {
        currentLevel: 0
      }
    
      constructor(props) {
        super(props)
    
        this.state = {
          currentLevel: 0,
          isFirstRender: false
        }
      }
    
      static getDerivedStateFromProps(nextProps, prevProps) {
        if (!prevProsp.isFirstRender) {
          return {
            currentLevel: nextProps.currentLevel,
            isFirstRender: true
          };
        }
    
        return null;
      }
    
      _handleInputChange = e => {
        this.setState({
          currentLevel: e.target.value
        })
      }
    
      render() {
        const { currentLevel } = this.state
    
        return (
            <input
              placeholder={0}
              value={currentLevel}
              onChange={this._handleInputChange}
            />
        )
      }
    }
    
    export default NameEditor
    

    因此,在这种情况下,您将有机会通过手动输入表单中的值来操作组件状态。

    解决方案 #2(无需重新挂载)

    尝试设置一些标志来分隔每次重新渲染时的外部 (getDerived...) 和内部 (Controlled Comp...) 状态更新。例如updateType:

    import PropTypes from 'prop-types'
    import React from 'react'
    
    export class NameEditor extends React.Component {
      static propTypes = {
        currentLevel: PropTypes.number
      }
    
      static defaultProps = {
        currentLevel: 0
      }
    
      constructor(props) {
        super(props)
    
        this.state = {
          currentLevel: 0,
          updateType: 'props' // by default we expecting update by incoming props
        } 
      }
    
      static getDerivedStateFromProps(nextProps, prevProps) {
        if (!prevState.updateType || prevState.updateType === 'props') {
          return {
            updateType: 'props',
            currentLevel: nextProps.currentLevel,
            exp: nextProps.exp
          }
        }
    
        if (prevState.updateType === 'state') {
          return {
            updateType: '' // reset flag to allow update from incoming props
          }
        }
    
        return null
      }
    
      _handleInputChange = e => {
        this.setState({
          currentLevel: e.target.value
        })
      }
    
      render() {
        const { currentLevel } = this.state
    
        return (
            <input
              placeholder={0}
              value={currentLevel}
              onChange={this._handleInputChange}
            />
        )
      }
    }
    
    export default NameEditor
    

    附: 这可能是一种反模式(希望 Dan 永远不会看到这一点),但我现在无法在脑海中找到更好的解决方案。

    解决方案#3

    参见 Sultan H. 这篇文章下的帖子,关于带有来自包装器组件的显式回调的受控逻辑。

    【讨论】:

    • 非常有趣...谢谢,应该试试。
    【解决方案2】:

    更新:

    目前_handleInputChange方法只会修改子组件的状态,会调用getDerivedStateFromProps

    该方法的工作方式是,当每个 newPropssetState 调用发生时都会调用它。

    因此,行为如下:

    1. 您可以使用处理程序更改值。
    2. getDerivedStateFromProps get 被调用,它将从父组件中获取 currentLevel 值,由于我们没有在那里进行任何更改,因此它仍然没有被修改,因此,它将覆盖来自调用你的处理程序的新值,使用存在于父组件中且未被修改的值。

    要解决这个问题:我们需要一个来自父组件的回调函数,它的作用与 handleInputChange 相同。

    所以:

    1. 给你的父组件添加一个handleCurrentLevelChange方法,它只有一个参数e.target.value,它的作用是在你的状态下修改currentLevel
    2. 将您创建的handleCurrentLevelChange 传递给您的NameEditor您想要的名称,可能是相同的名称。
    3. 如下修改您的孩子的句柄:
      _handleInputChange = (e, cb) => {
        this.setState({
          currentLevel: e.target.value
        }, () => {
          cb && cb(e.target.value) //this makes the callback optional.
        });
      }
    
    1. 修改您的 onChange 属性以适应新的更新:
            <input
              placeholder={0}
              value={currentLevel}
              onChange={(e) => this._handleInputChange(e, handleCurrentLevelChange)}
    

    onChange 属性和处理程序的新行为将允许更改同时发生在您的孩子和您的父母身上。

    这应该可以解决当前的问题。

    【讨论】:

    • 有趣的想法,但我试图弄清楚如何单独处理组件以解决此类问题。
    • 谢谢!我从一开始就理解你,但重点不是将逻辑带到原始组件之外(不对父组件进行任何改进)。但无论如何,这是个好主意!
    • 太好了,很抱歉我没有从一开始就抓住你想要的东西,我很高兴你找到了你真正需要的东西。祝你好运@MaxTravis
    【解决方案3】:

    因为在设置状态后 React 调用渲染,但在渲染之前你总是调用 getDerivedStateFromProps 方法。

    setState 计划更新组件的状态对象。当状态改变时,组件通过重新渲染来响应

    getDerivedStateFromProps 在调用 render 方法之前调用,无论是在初始挂载时还是在后续更新时。它应该返回一个对象来更新状态,或者返回 null 以不更新任何内容。

    https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops

    【讨论】:

    • 是的,我还阅读了关闭文档。这与上述解决方案无关。
    • @MaxTravis 您可以添加条件并检查道具和状态,如果您不需要重新渲染此组件,则返回 null
    • 请在您的帖子中分享您的想法。仅在关闭文档上提供参考并不是真正的帮助人。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-10
    • 2020-10-24
    • 1970-01-01
    相关资源
    最近更新 更多