我在一页上有100个Post组件。每个都有状态isActive
。我还在此页面上单独列出了这些帖子的标题。如果我单击列表中的项目,那么我想将Post Component的状态更改为isActive:true,将其余组件的状态更改为isActive:false。
我在Post组件之上有Posts组件,因此我应该在Posts组件中遍历所有Post组件,并将更改状态设置为isActive:false,而无需当前单击的Post组件?
在这种情况下不是那么夸张吗?有效率吗?我使用Redux,所以也许会有所帮助?
class Posts extends Component {
state = {
isActive: this.props.isActive
}
render() {
return (
<div>
{this.props.posts.map((post, index) =>
<Post isActive={this.state.isActive} key={index} />
)}
</div>
);
}
}
const mapStateToProps = state => {
return {
posts: state.posts.posts,
isActive: state.posts.isActive
}
}
export default connect(mapStateToProps, null)(Posts);
class Post extends Component {
state = {
isActive: this.props.isActive
}
onClick = () => {
this.setState(state => ({
isActive: !state.isActive
}));
}
render() {
return (
<div className={this.state.isActive ? "show" : ""} onClick={this.onClick}>
Post
</div>
);
}
}
export default Post;
class List extends Component {
onClick = () => {
//here I would like to set isActive:true to current clicked Post and isActive:false to rest of Posts
}
render() {
return (
<ul>
{this.props.posts.map((post, index) =>
<li onClick={this.onClick}>{post.name}</li>
)}
</ul>
);
}
}
const mapStateToProps = state => {
return {
posts: state.posts.posts
}
}
export default connect(mapStateToProps, null)(List);
答案 0 :(得分:1)
您可以只存储selectedID(或索引)并使用简单条件,例如:
class Posts extends Component {
render() {
return (
<div>
{this.props.posts.map((post, index) =>
<Post isActive={this.props.selectedIDX === index} post={post} />
)}
</div>
);
}
}
// connect with posts and selectedIDX
class List extends Component {
constructor (props) {
super(props)
this.onClickHandler = this.onClickHandler.bind(this);
}
onClickHandler = (id) => {
this.props.actionToSetSelectedIDX( id );
}
render() {
return (
<ul>
{this.props.posts.map((post, index) =>
<li onClick={(e, index) => this.onClickHandler(index) }>{post.name}</li>
)}
</ul>
);
}
// connect with posts, selectedIDX and actionToSetSelectedIDX
答案 1 :(得分:1)
这里是一个简单的应用程序,仅位于一个文件中:)您可以阅读评论并尝试了解发生了什么。它将使您了解如何在商店中保持status
状态,调度动作并更新状态。
您将看到,Post
和List
组件没有任何状态。它们只是愚蠢的组件。父组件Posts
呈现它们。
您可以看到一个有效的示例here,进行分叉并播放。此示例有单独的目录和文件。我只是将所有内容都放在一个文件中,以将其正确移动到此处。我不能保证将沙箱保留得太久,所以您可能要立即分叉它:)
PS:这是午夜的乐趣。它可能不包括最佳做法:)
import React from "react";
import ReactDOM from "react-dom";
import { Provider, connect } from "react-redux";
import { createStore, combineReducers } from "redux";
// To use styles in a single file. You can use a .css file to define those
// and use className instead of styles in the Post component
const styles = {
post: { border: "1px solid gray", marginTop: "-1px" },
show: { backgroundColor: "silver"},
}
// Posts is the parent component. It renders Post and List component by
// mapping the posts.
class Posts extends React.Component {
// This method changes the status state by passing a post to
// the action creator
handleStatus = post => this.props.changeStatus(post);
// This method maps the posts and renders Post components for each post.
// We are passing the post and isActive boolean.
getPosts() {
return this.props.posts.map(post => {
// We are getting the isActive by checking the status state
// if it has our post's id.
const isActive = this.props.status[post.id];
return <Post key={post.id} post={post} isActive={isActive} />;
});
}
// This method renders our List items, li's. Again, we are passing the
// post and our handleStatus method to change the status.
getList() {
return this.props.posts.map(post => (
<List key={post.id} post={post} handleStatus={this.handleStatus} />
));
}
render() {
return (
<div>
{this.getPosts()}
<ul>{this.getList()}</ul>
</div>
);
}
}
// Post is a stateless, dumb component. It just renders the post item.
const Post = props => {
const { id, title } = props.post;
const { isActive } = props;
// We check the isActive and if it is true then add a show class.
let classes = styles.post;
if (isActive) {
classes = { ...classes, ...styles.show };
}
return (
<div style={classes}>
<p>ID: {id} </p>
<p>Title: {title}</p>
</div>
);
};
// List is a stateless, dumb component just renders li's. But, it has
// handleStatus prop. By onClick and using another method, we are
// passing our post back to the parent, to the handleStatus method
const List = props => {
const { post, handleStatus } = props;
const changeStatus = () => handleStatus(post);
return <li onClick={changeStatus}>{post.title}</li>;
};
// We open our state to our component.
const mapStateToProps = state => ({
posts: state.posts.posts,
status: state.posts.status,
});
// This is our action creator, takes the post as parameter and
// use it as the payload.
const changeStatus = post => ({
type: types.CHANGE_STATUS,
post,
});
// Connecting our component.
const ConnectedPosts = connect(
mapStateToProps,
{ changeStatus },
)(Posts);
// Just defining our action creator types, avoiding magic strings.
const types = {
CHANGE_STATUS: "CHANGE_STATUS",
};
// This is our reducer. We have one posts property and one status in our state.
const initialState = {
posts: [
{ id: "1", title: "foo" },
{ id: "2", title: "bar" },
{ id: "3", title: "baz" },
],
status: {},
};
// Our reducer takes the post and adds post's id in our status state.
// In the initial state they are all undefined, so not true. In the first
// click, we change to true for each post. If we click again, we change it to
// false without mutating our original state.
const posts = (state = initialState, action) => {
switch (action.type) {
case types.CHANGE_STATUS: {
const {
post: { id },
} = action;
const newStatus = { ...state.status, [id]: !state.status[id] };
return { ...state, status: newStatus };
}
default:
return state;
}
};
// Our store.
const rootReducer = combineReducers({
posts,
});
const store = createStore(rootReducer);
// Rendering our app.
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<ConnectedPosts />
</Provider>,
rootElement,
);