我正在编写一个ControlledInput
组件,并且为了使用ControlledInput
访问该组件的状态,我在binder
中有一个ControlledInput
道具。 / p>
使用该组件时出现了一个小问题:
render() {
const CI = props => <ControlledInput binder={this} {...props} />;
return (
<div style={styles.container}>
<h1>NEW RECIPE</h1>
<ControlledInput binder={this} label={"Title"} />
</div>
);
}
以上实现完全正常。但是,请注意我定义的const CI
。我尝试使用它,所以我可以只写<CI label={"Title"}/>
而不必写binder
,因为在给定的{{1 }} 方法。
使用binder
的问题在于,当我在输入中键入内容时,输入“模糊”并且必须重新选择它。这似乎是因为ControlledInput
方法在每个渲染上都创建了render
。
我希望我已经清楚地解释了这一点,因为我的头很痛。
无论如何,为什么发生这种情况对我来说很有意义。而且我知道一种解决方案是将<CI label={"Title"}/>
放在render
函数之外。但是然后我不得不将其称为CI
,这开始破坏目的。
我不能将const CI = props => <ControlledInput binder={this} {...props} />;
放在全局范围内,因为那样我就无法访问render
。
这是<this.CI>
的当前代码(正在进行中):
CI
整个工作的重点是简化创建具有受控组件的表单,避免在每个表单中添加this
和ControlledInput
,我认为这不是DRY。
然后我想通过不将// @flow
import React, { Component } from "react";
type Props = {
containerStyle?: Object,
label: string,
propName?: string,
binder: Component<Object, Object>,
onChange?: Object => void
};
class ControlledInput extends Component<Props> {
render() {
const props = this.props;
const propName = props.propName || props.label.toLowerCase();
return (
<div style={props.containerStyle}>
<p>{props.label}</p>
<input
type="text"
label={props.label}
onChange={
this.props.onChange ||
(e => {
props.binder.setState({ [propName]: e.target.value });
})
}
value={props.binder.state[propName]}
></input>
</div>
);
}
}
传递给每个组件来获得更多的DRY,这就是为什么我正在做value={this.state.whatever}
,这又必须在类内部才能访问{{ 1}},并且在onChange={e=>this.setState({whatever: e})}
函数内部被称为binder={this}
,而不是const CI = props => <ControlledInput binder={this} {...props} />;
。
第一个解释为什么你需要通过this
,尽管我想我也可以有像render
这样的道具,在那种情况下,将它们组合成类似的东西确实有意义CI
,如@John Ruddell所建议。
请注意,我已经提供了覆盖this.CI
的可能性,并计划对大多数或所有其他道具(例如this
)进行覆盖。 (尤其是setState={this.setState} parentState={this.state}
和{...propsToSend}
),确实会使组件的可重用性降低,但是主要用例是快速创建没有特殊输入处理的多个输入。
因此,再次,我的理想情况是调用onChange
并让组件代码负责正确绑定value={this.props.value || binder.state[propName]}
和value
。如果可能的话。然后,第二种选择是在一个位置上定义一个必要的上下文道具,以便在需要多次实际使用组件时变得很简单,就像这样:
onChange
我听说访问父状态/上下文可能是一种反模式,但是必须有某种方法可以在不使用反模式的情况下做我想做的事情,是吗?
答案 0 :(得分:1)
如果您想要父级的状态,请在那里处理状态并将值传递给您的输入-ControlledInput
除了知道如何处理数据的输入和输出外,什么都不知道。像这样,请注意,我稍微加了些名称,以便您可以看到哪个组件正在处理什么:
import React, { useState } from "react"
const Parent = () => {
const [title, setTitle] = useState("")
const handleChangeInParent = (newTitle) => {
setTitle((oldValue) => newTitle)
}
return(<div style={styles.container}>
<h1>NEW RECIPE</h1>
<ControlledInput handleChange={handleChangeInParent} label={title} />
</div>)
}
const ControlledInput = ({handleChange, label}) => {
return (
<input onChange={handleChange} type="text" value={label} />
)
}
如果ControlledComponent
需要处理自己的状态,则将其传递为默认值,然后让Parent
在保存(或其他任何内容)时读取该值:
import React, { useState } from "react"
const Parent = () => {
const handleSaveInParent = (newTitle) => {
console.log("got the new title!")
}
return (
<div style={styles.container}>
<h1>NEW RECIPE</h1>
<ControlledInput handleSave={handleSaveInParent} initialLabel="Title" />
</div>
)
}
const ControlledInput = ({ handleSave, initialLabel }) => {
const [title, setTitle] = useState(initialLabel)
const handleChange = (ev) => {
const value = ev.target.value
setTitle((oldValue) => value)
}
const handleSubmit = (ev) => {
ev.preventDefault()
handleSave(title)
}
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} type="text" value={title} />
</form>
)
}
您不应该通过发送this
-只需发送值和/或函数来处理值。
答案 1 :(得分:0)
从技术上讲这不是一个肯定的答案,但是我已经重写了该组件,使其采用了state
和(已更新)一个setterFn
的道具:>
// @flow
import React, { Component } from "react";
type Props = {
containerStyle?: Object,
labelStyle?: Object,
label: string,
propName?: string,
state: Object,
onChange?: Object => void,
textArea?: boolean,
setterFn: (key: string, value: mixed) => void
};
class ControlledInput extends Component<Props> {
render() {
const props = this.props;
const propertyName = props.propName || props.label.toLowerCase();
const TagType = props.textArea ? "textarea" : "input";
// only pass valid props to DOM element (remove any problematic custom props)
const { setterFn, propName, textArea, ...domProps } = props;
return (
<div style={props.containerStyle}>
<p style={props.labelStyle}>{props.label}</p>
<TagType
{...domProps}
label={props.label} // actually could get passed automatically, but it's important so I'm leaving it in the code
onChange={
this.props.onChange ||
(setterFn ? e => setterFn(propertyName, e.target.value) : null)
}
value={props.state[propertyName] || ""}
></TagType>
</div>
);
}
}
export default ControlledInput;
class Wrapper extends Component<Object, Object> {
state = {};
render() {
const setterFn = (k, v) => this.setState({ [k]: v });
const p = { state: this.state, setterFn: setterFn.bind(this) };
return <ControlledInput {...p} {...this.props.inputProps} />
}
}
我想这更合适。它仍然比binder={this}
占用更多的空间。
实际上不是以下问题:
如何从组件访问父级状态。尽管从评论看来,这似乎是一种反模式,但我确实从React的理论中了解了这一点。
如何在其他地方设置这些重复道具,以便我可以调用`。我猜唯一的解决方案是做这样的事情:
render() {
const props = {state: this.state, setState: this.setState}
<ControlledInput {...props} label="Title"/>
}
这当然不是一个糟糕的解决方案。尤其是如果我将该名称缩写为一个字符。
非常感谢@John Ruddell将我设置在正确的道路上。