我试图简单地映射从api返回的一些数据,并为返回的每个对象创建一个无状态组件。我希望能够单击任何组件来切换其其余数据的可见性。
我已经尝试了许多方法来做到这一点并不断撞墙,我也一直在寻找堆栈溢出的问题,而且似乎找不到答案。
我已经通过使它们成为单独的类组件来使其工作,但是,似乎只有很多切换功能才需要很多代码。
在此先感谢您的帮助或见解,以下是我的简要介绍。
为澄清起见,这是我学习使用react和外部api的简单应用,它未使用redux。
以类组件的状态获取用户
class PersonList extends Component {
constructor(props) {
super(props);
this.state = {
resource: []
};
}
async componentDidMount() {
let fetchedData = await API_Call("people");
this.setState({ resource: fetchedData.results });
while (fetchedData.next) {
let req = await fetch(fetchedData.next);
fetchedData = await req.json();
this.setState({
resource: [...this.state.resource, ...fetchedData.results]
});
}
}
}
然后在结果上映射并为每个结果呈现一个组件
render() {
const mappedPeople = this.state.resource.map((person, i) => (
<Person key={i} {...person} />
));
return <div>{mappedPeople}</div>;
}
我可以在其中使每个人组件成为无状态组件,并且可以单击它并显示其余数据吗?这是我目前所拥有的。
class Person extends Component {
constructor(props) {
super(props);
this.state = {
visibility: false
};
}
toggleVisible = () => {
this.setState(prevState => ({
visibility: !prevState.visibility
}));
};
render() {
return (
<div>
<h1 onClick={this.toggleVisible}>{this.props.name}</h1>
{this.state.visibility && (
<div>
<p>{this.props.height}</p>
</div>
)}
</div>
);
}
}
在此先感谢您的见解或帮助!
答案 0 :(得分:2)
您可以在父组件中保留一个对象visible
,该对象将具有表示人员索引的键和一个值,该值指示该人员是否可见。这样,您可以在此单个对象中切换人的索引,而不必使用有状态的子组件。
示例
class PersonList extends Component {
constructor(props) {
super(props);
this.state = {
resource: [],
visible: {}
};
}
// ...
toggleVisibility = index => {
this.setState(previousState => {
const visible = { ...previousState.visibile };
visible[index] = !visible[index];
return { visible };
});
};
render() {
const mappedPeople = this.state.resource.map((person, i) => (
<Person
key={i}
{...person}
visible={this.state.visible[i]}
onClick={() => this.toggleVisibility(i)}
/>
));
return <div>{mappedPeople}</div>;
}
}
const Person = (props) => (
<div>
<h1 onClick={props.onClick}>{props.name}</h1>
{props.visible && (
<div>
<p>{props.height}</p>
</div>
)}
</div>
);
答案 1 :(得分:1)
与@Tholle类似的想法,但是方法不同。假设id
对象中有一个person
,我们将更改visibles
的状态并切换id
。
class PersonList extends React.Component {
constructor(props) {
super(props)
this.state = {
resource: this.props.persons,
visibles: {},
}
}
toggleVisible = id => this.setState( prevState => ({
visibles: { ...prevState.visibles, [id]: !prevState.visibles[id] },
}))
render() {
const mappedPeople =
this.state.resource.map((person, i) =>
<Person
key={person.id}
visibles={this.state.visibles}
toggleVisible={this.toggleVisible}
{...person}
/>
)
return (
<div>
{mappedPeople}
</div>
)
}
}
const Person = (props) => {
const handleVisible = () =>
props.toggleVisible( props.id );
return (
<div>
<h1 onClick={handleVisible}>
{props.name}</h1>
{props.visibles[props.id] &&
<div>
<p>{props.height}</p>
</div>
}
</div>
);
}
const persons = [
{ id: 1, name: "foo", height: 10 },
{ id: 2, name: "bar", height: 20 },
{ id: 3, name: "baz", height: 30 },
]
const rootElement = document.getElementById("root");
ReactDOM.render(<PersonList persons={persons} />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
答案 2 :(得分:0)
您可以确保您的“ this.state.resource”数组在每个对象上都具有可见性标志:
this.state.resource = [
{ ..., visibility: true },
{ ..., visibility: false}
...
];
通过稍微修改获取内容来完成此操作。
let fetchedData = await API_Call("people");
this.setState({
resource: fetchedData.results.map(p => ({...p, visiblity: true}))
});
将您的Person组件重新合并到PersonList中(就像您尝试做的那样),然后在onclick上执行以下操作:
onClick={() => this.toggleVisible(i)}
更改toggleVisible()函数以执行以下操作。
toggleVisible = (idx) => {
const personList = this.state.resource;
personList[idx].visibility = !personList[idx].visibility;
this.setState({ resource: personList });
}
现在,当您执行操作时:
this.state.resource.map((person, i) => ...
...您可以访问“ person.visibility”,并且onclick会切换被点击的特定索引。
不过,我认为这直接回答了您的问题...
我将继续将Person分解为自己的组件,这确实是一个好习惯!
除了更好的组织之外,主要原因之一是避免在道具中使用lamda(我实际上在上面做了)。由于您需要为每个索引执行一次onClick,因此您需要使用数据属性,或者实际上对每个人项目使用React.Component。 您可以在这里进行一些研究: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
顺便说一句,您仍然可以创建不是这样的“ React.Component”的“组件”:
import React from 'react';
const Person = ({ exProp1, exProp2, exProp3}) => {
return <div>{exProp1 + exProp2 + exProp3}</div>
}
Person.propTypes = {
...
}
export default Person;
如您所见,React.Component没有继承任何东西,因此您将两全其美(创建组件而不创建“ Components”)。我倾向于这种方法,而不是将所有内容都内联。但是,如果您的应用程序不是很大,而您只想完成它,那么采用第一种方法并不是很糟糕。