【问题标题】:optional controlled / uncontrolled React component with conditional React hooks带有条件 React 钩子的可选受控/非受控 React 组件
【发布时间】:2019-09-07 20:46:43
【问题描述】:

IMO,React Hooks useState 非常适合从 props 中选择使用 value 或使用自己的 state 的模式,但是当我有条件地使用 hook 时,lint 显示了一些错误。

Working Example

我尝试使用条件如下的钩子,但出现 eslint 错误 React hook useState is called conditionally。根据这个explanation from docReact relies on the order in which Hooks are called

const Counter = ({ value, onChange, defaultValue = 0 }) => {
  const [count, onCountChange] =
    typeof value === "undefined" ? useState(defaultValue) : [value, onChange];
  return (
    <div>
      {count.toString()}
      <button
        onClick={() => {
          onCountChange(count + 1);
        }}
      >
        +
      </button>
    </div>
  );
};
function App() {
  const [count, onCountChange] = useState(0);
  return (
    <div className="App">
      <div>
        Uncontrolled Counter
        <Counter />
      </div>
      <div>
        Controlled Counter
        <Counter value={count} onChange={onCountChange} />
      </div>
    </div>
  );
}

如何使用钩子来实现与以下类 Component 相同的功能?

class CounterClass extends React.Component {
  state = {
    value: this.props.defaultValue || 0
  };
  render() {
    const isControlled = typeof this.props.defaultValue === "undefined";
    const count = isControlled ? this.props.value : this.state.value;

    return (
      <div>
        {count.toString()}
        <button
          onClick={() => {
            isControlled &&
              this.props.onChange &&
              this.props.onChange(this.props.value + 1);
            !isControlled && this.setState({ value: this.state.value + 1 });
          }}
        >
          +
        </button>
      </div>
    );
  }
}

或者这种 props/state 在一个组件中的可选方式是错误的?

我从 React JSX &lt;input&gt; 组件中学习了 "defaultValue""value""onChange" API 命名和想法。

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    您可以将组件拆分为完全受控和完全不受控。 Demo

    const CounterRepresentation = ({ value, onChange }) => (
      <div>
        {value.toString()}
        <button
          onClick={() => {
            onChange(value + 1);
          }}
        >
          +
        </button>
      </div>
    );
    
    const Uncontrolled = ({ defaultValue = 0 }) => {
      const [value, onChange] = useState(defaultValue);
      return <CounterRepresentation value={value} onChange={onChange} />;
    };
    
    // Either use representation directly or uncontrolled
    const Counter = ({ value, onChange, defaultValue = 0 }) => {
      return typeof value === "undefined" ? (
        <Uncontrolled defaultValue={defaultValue} />
      ) : (
        <CounterRepresentation value={value} onChange={onChange} />
      );
    };
    

    【讨论】:

    • 哇!!我认为这是我需要的正确答案。我认为这是使用representational componentcontainer component(或智能/转储组件)概念的一个很好的例子。
    【解决方案2】:

    好问题!我认为这可以通过使useState 无条件调用并仅在您决定渲染哪个状态以及使用什么更改处理程序时使部分有条件来解决。

    我刚刚发布了一个可以解决这个问题的钩子:use-optionally-controlled-state

    用法:

    import useOptionallyControlledState from 'use-optionally-controlled-state';
     
    function Expander({
      expanded: controlledExpanded,
      initialExpanded = false,
      onChange
    }) {
      const [expanded, setExpanded] = useOptionallyControlledState({
        controlledValue: controlledExpanded,
        initialValue: initialExpanded,
        onChange
      });
     
      function onToggle() {
        setExpanded(!expanded);
      }
     
      return (
        <>
          <button onClick={onToggle} type="button">
            Toggle
          </button>
          {expanded && <div>{children}</div>}
        </>
      );
    }
    
    // Usage of the component:
    
    // Controlled
    <Expander expanded={expanded} onChange={onExpandedChange} />
     
    // Uncontrolled using the default value for the `initialExpanded` prop
    <Expander />
     
    // Uncontrolled, but with a change handler if the owner wants to be notified
    <Expander initialExpanded onChange={onExpandedChange} />
    

    通过使用钩子来实现这一点,您不必包装额外的组件,理论上您可以将其应用于同一组件内的多个道具(例如,&lt;Prompt isOpen={isOpen} inputValue={inputValue} /&gt; 组件,其中两个道具都可以选择控制)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-20
      • 2021-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多