带有条件 React 钩子的可选受控/不受控 React 组件
optional controlled / uncontrolled React component with conditional React hooks
IMO,React Hooks useState 非常适合可选使用 props 的值或使用自己的状态的模式,但是当我有条件地使用 hook 时,lint 显示了一些错误。
我尝试使用条件如下的钩子,但出现 eslint 错误 React hook useState is called conditionally
。根据这个explanation from doc,React 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>
);
}
如何使用钩子实现与下面 class 组件相同的功能?
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 <input>
组件中学到了 "defaultValue"
、"value"
、"onChange"
API 命名和想法。
您可以将您的组件拆分为完全受控和完全不受控。 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} />
);
};
好问题!我认为这可以通过挂钩解决,方法是使 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} />
通过使用钩子来实现这一点,您不必包装额外的组件,理论上您可以将其应用于同一组件内的多个道具(例如 <Prompt isOpen={isOpen} inputValue={inputValue} />
组件,其中两个道具都是可选控制)。
IMO,React Hooks useState 非常适合可选使用 props 的值或使用自己的状态的模式,但是当我有条件地使用 hook 时,lint 显示了一些错误。
我尝试使用条件如下的钩子,但出现 eslint 错误 React hook useState is called conditionally
。根据这个explanation from doc,React 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>
);
}
如何使用钩子实现与下面 class 组件相同的功能?
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 <input>
组件中学到了 "defaultValue"
、"value"
、"onChange"
API 命名和想法。
您可以将您的组件拆分为完全受控和完全不受控。 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} />
);
};
好问题!我认为这可以通过挂钩解决,方法是使 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} />
通过使用钩子来实现这一点,您不必包装额外的组件,理论上您可以将其应用于同一组件内的多个道具(例如 <Prompt isOpen={isOpen} inputValue={inputValue} />
组件,其中两个道具都是可选控制)。