什么是React中的受控组件

45 阅读4分钟

有很多关于React的文章都在谈论受控组件和非受控组件,却没有对它们进行解释。我的文章也是如此,而我总是试图添加至少一两句话来解释它们,但最后,我想如果能有一个简短的教程来展示React中受控组件的一个简单例子就更好了。

让我们来看看下面这个输入框元素,它被渲染在我们的功能组件中。尽管输入框是这里的非控制输入元素,但我们通常指的是包围的App组件是非控制组件。

import React from 'react';

const App = () => (
  <div>
    <label>
      My uncontrolled Input: <input type="text" />
    </label>
  </div>
);

export default App;

注意:对于受控或不受控元素来说,组件本身是一个函数还是类组件并不重要。不受控制的元素--如文本输入、复选框、单选按钮和整个带有输入的表单--总是可以不受控制或受控制。

这是一个不受控制的输入字段,因为一旦你启动应用程序,你可以在字段中输入一些东西,并看到变化,尽管我们在源代码中没有给出任何指示。没有写任何行来显示输入字段中的值,也没有写任何行来在我们向其中输入东西时改变值。毕竟,这是因为我们在这里处理的是HTML,而且这也是输入框的原生行为,因为它管理着自己的内部状态。

不受控制的组件与受控制的组件

让我们看看另一个案例,在这个案例中,我们并不清楚我们是在处理一个不受控还是受控的组件。下一个例子在我们的函数组件中加入了React Hooks状态管理

import React, { useState } from 'react';

const App = () => {
  const [value, setValue] = useState('');

  const handleChange = event => setValue(event.target.value);

  return (
    <div>
      <label>
        My still uncontrolled Input:
        <input type="text" onChange={handleChange} />
      </label>

      <p>
        <strong>Output:</strong> {value}
      </p>
    </div>
  );
};

export default App;

我们还将当前值作为输出显示。问问你自己。为什么这个组件(元素)还是不受控制的?当你启动应用程序时,输入栏显示的值与输出段相同。这应该是没有问题的,不是吗?让我们看看为什么不是这样。试试用下面的初始状态代替。

import React, { useState } from 'react';

const App = () => {
  const [value, setValue] = useState('Hello React');

  const handleChange = event => setValue(event.target.value);

  return (
    <div>
      <label>
        My still uncontrolled Input:
        <input type="text" onChange={handleChange} />
      </label>

      <p>
        <strong>Output:</strong> {value}
      </p>
    </div>
  );
};

export default App;

现在你可以看到区别了。当输入字段显示一个空字段时,输出段落显示初始状态。只有当你开始在输入框中输入时,两个元素似乎才会同步,但它们并没有,因为输入框仍然跟踪它自己的内部状态,而输出段是由来自处理函数的实际React状态驱动。因此,即使它们在你开始打字时输出相同,但值的底层来源是不同的:

  • 输入字段从内部DOM节点状态接收其值
  • 输出段从React的状态中获得其值

在你的React应用程序中拥有一个不受控制的元素/组件会导致不需要的行为,从而导致bug。你想从一个真理的来源来驱动你的UI;在React中,这应该是props和state。给予一个组件相同的道具和状态,它应该总是呈现相同的输出:(props, state) => view

从不受控组件到受控组件

你可以通过自己控制它的值将输入从不受控变为受控。例如,在这种情况下,输入字段提供了一个值属性。

import React, { useState } from 'react';

const App = () => {
  const [value, setValue] = useState('Hello React');

  const handleChange = event => setValue(event.target.value);

  return (
    <div>
      <label>
        My controlled Input:
        <input type="text" value={value} onChange={handleChange} />
      </label>

      <p>
        <strong>Output:</strong> {value}
      </p>
    </div>
  );
};

export default App;

通过从React的状态中给输入值,它不再使用其内部状态,而是使用你从React提供的状态。现在,一旦你启动应用程序,输入栏和输出段都应该看到初始状态。同时,当你在输入栏中输入东西时,输入栏和输出段都会被React的状态所同步。输入栏已经成为一个受控元素,而App组件则是受控组件。你负责在你的用户界面上显示什么。你可以在这个GitHub资源库中看到不同的输入元素作为受控组件实现。