Mos*_*vah 198 javascript reactjs
我有以下结构:
FormEditor- 拥有多个FieldEditor
FieldEditor- 编辑表单的一个字段,并在其状态中保存有关它的各种值
当在FormEditor中单击一个按钮时,我希望能够收集有关所有FieldEditor组件中的字段的信息,状态中的信息,以及在FormEditor中的所有信息.
我考虑存储有关FieldEditor状态之外的字段的信息,并将其置于FormEditor状态.但是,当它们改变并将其信息存储在其状态时,需要FormEditor监听每个FieldEditor组件.
我不能只是访问孩子的州吗?这是理想的吗?
Mar*_*ime 174
在我详细介绍如何访问子组件的状态之前,请务必阅读Markus-ipse关于处理此特定方案的更好解决方案的答案.
如果您确实希望访问组件子项的状态,则可以为ref每个子项分配一个调用的属性.现在有两种方法可以实现引用:使用React.createRef()和回调引用.
React.createRef()目前,这是使用React 16.3中引用的推荐方法(有关详细信息,请参阅文档).如果您使用的是早期版本,请参阅下面有关回调参考的信息.
您需要在父组件的构造函数中创建一个新引用,然后通过该ref属性将其分配给子项.
class FormEditor extends React.Component {
constructor(props) {
super(props);
this.FieldEditor1 = React.createRef();
}
render() {
return <FieldEditor ref={this.FieldEditor1} />;
}
}
Run Code Online (Sandbox Code Playgroud)
要访问此类参考,您需要使用:
const currentFieldEditor1 = this.FieldEditor1.current;
Run Code Online (Sandbox Code Playgroud)
这将返回已安装组件的实例,以便您可以使用它currentFieldEditor1.state来访问该状态.
简单地说,如果您在DOM节点而不是组件(例如<div ref={this.divRef} />)上使用这些引用,那么this.divRef.current将返回底层DOM元素而不是组件实例.
此属性采用一个回调函数,该函数传递对附加组件的引用.在安装或卸载组件后立即执行此回调.
例如:
<FieldEditor
ref={(fieldEditor1) => {this.fieldEditor1 = fieldEditor1;}
{...props}
/>
Run Code Online (Sandbox Code Playgroud)
在这些示例中,引用存储在父组件上.要在代码中调用此组件,您可以使用:
this.fieldEditor1
Run Code Online (Sandbox Code Playgroud)
然后this.fieldEditor1.state用来获得状态.
有一点需要注意,在尝试访问它之前,请确保您的子组件已呈现^ _ ^
如上所述,如果您在DOM节点而不是组件(例如<div ref={(divRef) => {this.myDiv = divRef;}} />)上使用这些引用,那么this.divRef将返回底层DOM元素而不是组件实例.
如果您想了解有关React的ref属性的更多信息,请从Facebook 查看此页面.
请务必阅读" 不要过度使用参考 "部分,该部分说明您不应该使用孩子state来"让事情发生".
希望这有助于^ _ ^
编辑:添加React.createRef()创建引用的方法.删除了ES5代码.
Mar*_*pse 124
如果您已经拥有单个FieldEditors的onChange处理程序,我不明白为什么您不能将状态移动到FormEditor组件,只是将回调从那里传递给将更新父状态的FieldEditors.对我来说,这似乎是一种更React-y方式.
也许这样的事情:
class FieldEditor extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const text = event.target.value;
this.props.onChange(this.props.id, text);
}
render() {
return (
<div className="field-editor">
<input onChange={this.handleChange} value={this.props.value} />
</div>
);
}
}
class FormEditor extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleFieldChange = this.handleFieldChange.bind(this);
}
handleFieldChange(fieldId, value) {
this.setState({ [fieldId]: value });
}
render() {
const fields = this.props.fields.map(field => (
<FieldEditor
key={field}
id={field}
onChange={this.handleFieldChange}
value={this.state[field]}
/>
));
return (
<div>
{fields}
<div>{JSON.stringify(this.state)}</div>
</div>
);
}
}
// Convert to class component and add ability to dynamically add/remove fields by having it in state
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditor fields={fields} />;
};
ReactDOM.render(<App />, document.body);
Run Code Online (Sandbox Code Playgroud)
http://jsbin.com/qeyoxobixa/edit?js,output
编辑:而不是将FieldEditor元素作为子项传递给FormEditor我只需传递一个fieldIds列表,然后在FormEditor中创建它们.这样可以更容易地动态添加FieldEditors并使FormEditor的render方法更少winkey.
Jos*_*dal 24
现在是2020 年,你们中的很多人会来这里寻找类似的解决方案,但使用Hooks(它们很棒!)以及代码清洁度和语法方面的最新方法。
因此,正如之前的答案所述,解决此类问题的最佳方法是将状态保持在子组件之外fieldEditor。你可以通过多种方式做到这一点。
最“复杂”的是父和子都可以访问和修改的全局上下文(状态)。当组件在树层次结构中非常深时,这是一个很好的解决方案,因此在每个级别发送道具的成本很高。
在这种情况下我认为不值得,更简单的方法会给我们带来我们想要的结果,只需使用强大的React.useState().
如前所述,我们将处理更改并将子组件的数据存储fieldEditor在父组件中fieldForm。为此,我们将发送对将处理更改并将更改应用于fieldForm状态的函数的引用,您可以这样做:
function FieldForm({ fields }) {
const [fieldsValues, setFieldsValues] = React.useState({});
const handleChange = (event, fieldId) => {
let newFields = { ...fieldsValues };
newFields[fieldId] = event.target.value;
setFieldsValues(newFields);
};
return (
<div>
{fields.map(field => (
<FieldEditor
key={field}
id={field}
handleChange={handleChange}
value={fieldsValues[field]}
/>
))}
<div>{JSON.stringify(fieldsValues)}</div>
</div>
);
}
Run Code Online (Sandbox Code Playgroud)
请注意,React.useState({})将返回一个数组,其中位置 0 是调用时指定的值(在本例中为 Empty 对象),位置 1 是对修改该值的函数的引用。
现在有了子组件 ,FieldEditor您甚至不需要创建带有 return 语句的函数。带有箭头函数的精益常量就可以了!
const FieldEditor = ({ id, value, handleChange }) => (
<div className="field-editor">
<input onChange={event => handleChange(event, id)} value={value} />
</div>
);
Run Code Online (Sandbox Code Playgroud)
Aaaaand 我们完成了,仅此而已。仅通过这两个纤细的功能组件,我们的最终目标就是“访问”我们的孩子FieldEditor价值并在我们的父母中展示它。
你可以查看 5 年前接受的答案,看看 Hooks 如何使 React 代码更精简(很多!)。
希望我的回答能帮助你更多地了解和了解 Hooks,如果你想在这里查看一个工作示例,它是.
小智 20
正如前面的答案所说,尝试将状态移动到顶部组件并通过传递给其子组件的回调来修改状态。
如果你真的需要访问被声明为功能组件(钩子)的子状态,你可以在父组件中声明一个ref,然后将它作为ref属性传递给子组件,但你需要使用React .forwardRef然后钩子useImperativeHandle来声明一个可以在父组件中调用的函数。
看看下面的例子:
const Parent = () => {
const myRef = useRef();
return <Child ref={myRef} />;
}
const Child = React.forwardRef((props, ref) => {
const [myState, setMyState] = useState('This is my state!');
useImperativeHandle(ref, () => ({getMyState: () => {return myState}}), [myState]);
})
Run Code Online (Sandbox Code Playgroud)
然后你应该能够通过调用在 Parent 组件中获取 myState :
myRef.current.getMyState();
现在您可以访问 InputField 的状态,它是 FormEditor 的子级。
基本上,每当输入字段(子项)的状态发生变化时,我们都会从事件对象中获取值,然后将该值传递给父级,在父级中设置了父级的状态。
单击按钮时,我们只是打印输入字段的状态。
这里的关键点是我们使用 props 来获取输入字段的id/value,并在我们生成可重用的子输入字段时调用在输入字段上设置为属性的函数。
class InputField extends React.Component{
handleChange = (event)=> {
const val = event.target.value;
this.props.onChange(this.props.id , val);
}
render() {
return(
<div>
<input type="text" onChange={this.handleChange} value={this.props.value}/>
<br/><br/>
</div>
);
}
}
class FormEditorParent extends React.Component {
state = {};
handleFieldChange = (inputFieldId , inputFieldValue) => {
this.setState({[inputFieldId]:inputFieldValue});
}
// On a button click, simply get the state of the input field
handleClick = ()=>{
console.log(JSON.stringify(this.state));
}
render() {
const fields = this.props.fields.map(field => (
<InputField
key={field}
id={field}
onChange={this.handleFieldChange}
value={this.state[field]}
/>
));
return (
<div>
<div>
<button onClick={this.handleClick}>Click Me</button>
</div>
<div>
{fields}
</div>
</div>
);
}
}
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditorParent fields={fields} />;
};
ReactDOM.render(<App/>, mountNode);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
140841 次 |
| 最近记录: |