我想这样填充状态:
state = {
id: '1',
parentId: null,
text: '', // state of the root input
child: [
{
id: '1.1',
parentId: '1',
text: '', // State of the first child input
child: null // First child didn't have any children
},
{
id: '1.2',
parentId: '1',
text: '', // State of the second child input
child: null // Second child didn't have any children
}],
};
故事:
这是一个递归问题。我正在做的是ul
,其中render
只有一项,即<Field />
组件。
<Field />
组件还具有一个状态,即名为id
的道具,并呈现input
元素和<Quantity />
组件。
<Quantity />
组件生成嵌套列表项,它们只是<Field />
组件。
生成的<Field />
组件具有正确的状态结构,例如正确的id
和parentId
,但是主要问题是嵌套的生成的组件具有自己的作用域,并不关心其父{{ 1}}组件状态。
我用错误的模式解决了这个问题吗?
我是否必须从一个有状态的组件<Field />
开始,它呈现一个无状态的<List />
组件,并将所有父状态作为道具传递给该<Field />
组件?
但是如何跟踪状态?
App.jsx:
<Field />
components / Field.jsx:
import React from 'react';
import {Field} from './components/Field';
const App = () => {
return (<ul className="list"><Field id='1'/></ul>);
};
export default App;
components / Quantity.jsx:
import React, {Component} from 'react';
import {Quantity} from './Quantity';
export class Field extends Component {
/**
* @type {{id: string, text: string, parentId: (string|null), child: ([]|null)}}
*/
state = {
id: this.props.id,
parentId: null,
child: null,
text: '',
};
/**
* Handle onchange behavior of the input
* @param target
*/
onChangeHandler = ({target}) => {
const {name, value} = target;
this.setState(prevState => {
return {[name]: value};
});
};
/**
* Trigger this function when Button of `Quantity component` is clicked
* @param {Number} quantity - Generate array of objects with properties
* and update state
*/
populateChild = (quantity) => {
// Construct new array of objects
const data = [...Array(quantity)].map((_, index) => {
// id => 1.1 .. 1.2 .. 1.3
// parentId => 1
const id = this.state.id + '.' + (index + 1);
return {...this.state, id: id, parentId: this.state.id}
});
this.setState(prevState => {
return {child: data};
});
};
render() {
return (<li>
<div className="wrap">
<div>
<label>Enter Text:</label>
<input type="text" name="text" onChange={this.onChangeHandler} value={this.state.text}/>
</div>
<div>
<Quantity funRef={this.populateChild}/>
<br/>
<br/>
</div>
</div>
{/* Generate New unordered list only if `child` property of the state is changed. */}
{this.state.child !== null &&
<ul>{this.state.child.map(element => <Field key={element.id} {...element}/>)}</ul>}
</li>);
};
}
答案 0 :(得分:0)
这里是my stackblitz solution,请注意,项目state
被管理到顶部App
组件中
我决定将项目存储到depth = 1的数组中,更容易处理更新:
items = [
{
id: "1",
parentId: null,
text: ""
},
{
id: "1.1",
parentId: "1",
text: ""
},
{
id: "1.2",
parentId: "1",
text: ""
}
];
这里是App
组件:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: defaultItems
};
}
handleChangeText = (id, text) => {
this.setState(prevState => ({
items: prevState.items.map(
item => item.id === id ? {...item, text} : item
)
}));
};
handleAddChildren = (parentId, quantity) => {
this.setState(prevState => {
const childrenItems = prevState.items
.filter(item => item.parentId === parentId);
const otherItems = prevState.items
.filter(item => item.parentId !== parentId);
const childrenText = childrenItems.map(item => item.text);
const newChildrenItems = Array.from(Array(quantity))
.map((_, index) => ({
parentId,
id: `${parentId}.${index + 1}`,
text: childrenText[index] || ""
}));
return {
items: [...otherItems, ...newChildrenItems]
};
});
};
getChildren = parentId => this.state.items.filter(
item => item.parentId === parentId
);
render() {
const {items} = this.state;
return (
<ul>
{
this.getChildren(null).map(item =>
<Field
key={item.id}
item={item}
getChildren={this.getChildren}
onChangeText={this.handleChangeText}
onAddChildren={this.handleAddChildren}
/>
)
}
</ul>
);
}
}
这是Field
组件:
export class Field extends Component {
handleChangeText = e => {
const {onChangeText, item} = this.props;
onChangeText(item.id, e.target.value);
};
handleGenerate = quantity => {
const {onAddChildren, item} = this.props;
onAddChildren(item.id, quantity);
};
render() {
const {item, getChildren, onChangeText, onAddChildren} = this.props;
const children = getChildren(item.id);
return (
<li>
<div className="wrap">
<div>
<label>Enter Text:</label>
<input
type="text"
name="text"
onChange={this.handleChangeText}
value={item.text}
/>
</div>
<div>
<Quantity onGenerate={this.handleGenerate} />
<br />
<br />
</div>
</div>
{children.length > 0 && (
<ul>
{children.map(item => (
<Field
key={item.id}
item={item}
getChildren={getChildren}
onChangeText={onChangeText}
onAddChildren={onAddChildren}
/>
))}
</ul>
)}
</li>
);
}
}
请注意,我们可以创建一个React context
以避免将属性getChildren
,onChangeText
,onAddChildren
传递给Field
组件。
这些属性实际上引用了顶部App
组件的功能,并向下传递给嵌套的Field
组件
此处the solution使用React.context
您只需要创建一个上下文:
const AppContext = React.createContext();
然后,您需要将上下文值存储到状态中,并用上下文包装您的App。现在,您只需要将道具key
和ìtem
传递到Field
组件。
constructor(props) {
super(props);
this.state = {
items: defaultItems,
contextValue: {
onChangeText: this.onChangeText,
onAddChildren: this.onAddChildren,
getChildren: this.getChildren
}
};
}
...
render() {
const {items, contextValue} = this.state;
const {Provider} = AppContext;
return (
<Provider value={contextValue}>
<ul>
{
this.getChildren(null).map(
item => <Field key={item.id} item={item}/>
)
}
</ul>
</Provider>
);
}
在Field
组件中,您可以使用static contextType = AppContext;
检索上下文,现在可以从上下文中检索函数onChangeText
,onAddChildren
,getChildren
< / p>
export class Field extends Component {
static contextType = AppContext;
handleChangeText = e => {
const {item} = this.props;
const {onChangeText} = this.context;
onChangeText(item.id, e.target.value);
};
handleGenerate = quantity => {
const {item} = this.props;
const {onAddChildren} = this.context;
onAddChildren(item.id, quantity);
};
render() {
const {item} = this.props;
const children = this.context.getChildren(item.id);
return (
<li>
<div className="wrap">
<div>
<label>Enter Text:</label>
<input
type="text"
name="text"
onChange={this.handleChangeText}
value={item.text}
/>
</div>
<div>
<Quantity onGenerate={this.handleGenerate} />
<br />
<br />
</div>
</div>
{children.length > 0 &&
<ul>{children.map(item => <Field key={item.id} item={item}/>)}</ul>
}
</li>
);
}
}