在反应中提升状态是否会产生对父级的依赖

does lifting up state in react create a dependency on the parent

我正在阅读关于 React 文档的 Lifting Up State 指南。

在我看来,此重构在 TemperatureInput 组件中创建了对其父组件的依赖,无论它是什么,以提供数据。

如果我想在我的应用程序中不需要这种数据绑定的其他地方使用 TemperatureInput 组件怎么办?对组件父级的依赖是否仍然存在,这意味着包含它的任何组件都需要提供该状态?

这似乎违反了数据应尽可能靠近其使用位置的想法。父级不一定需要知道该组件的值,但由于组件的设计,父级被迫定义并包含该值。

It seems to me that this refactor creates a dependency in the TemperatureInput component on its parent, whatever it is, to provide data.

确实如此。他们在这种情况下这样做的原因是因为他们希望在一个输入中输入的任何内容都反映在另一个输入中。 一些 组件将需要管理状态以保持两个链接,而parent 组件是最自然的地方。

正如他们所说,这被称为“提升状态”,但他们所做的另一种描述是将 TemperatureInput 更改为 controlled component。受控组件本身非常愚蠢,只是从 parent 组件获取指令。这使它们非常灵活,因为 parent 可以实现它喜欢的任何逻辑。但这确实意味着 parent 必须实现它。

与受控组件相反的是 uncontrolled component。不受控制的组件可能会从 parent 中获取一些初始值,但之后它们会自行处理事情。这使得它们不太灵活,因为它们必须已经具有内部功能,但这意味着它们通常可以更轻松地使用。

您可能遇到过受控组件与不受控组件的一个地方是标准 dom 元素,例如 <input>s。通过传递 valueonChange 道具,可以以受控方式使用输入,这导致它由 parent 组件控制。或者,您可以传入 defaultValue,让输入处理其余部分。


两种样式(受控和非受控)都有其用途,这仅取决于您的应用程序的需求是您应该为此组件使用什么。但您并不局限于其中一种:正如 <input> 组件所示,您可以在单个组件中支持 两种 操作模式。因此,如果您有一个页面需要链接温度输入,而另一个页面不需要,您可以向 TemperatureInput 添加一些额外的代码,以便它可以同时使用:

const scaleNames = {
  c: 'Celsius',
  f: 'Fahrenheit'
};

class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      temperature: props.defaultTemperature ?? ''
    };
  }

  handleChange(e) {
    if (this.props.onTemperatureChange) {
      // Prop exists. We're in controlled component mode.
      this.props.onTemperatureChange(e.target.value);
    } else {
      // Uncontrolled component
      this.setState({temperature: e.target.value});
    }
  }

  render() {
    // If the prop exists, use it (ie, controlled component)
    // Otherwise, use our state (uncontrolled component)
    const temperature = this.props.temperature ?? this.state.temperature;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={temperature} onChange={this.handleChange} />
      </fieldset>
    );
  }
}