Вот простое приложение в одном файле :) Вы можете прочитать комментарии и попытаться понять, что происходит. Это даст вам представление о том, как вы можете сохранять состояние status
в своем магазине, отправлять действие и обновлять свое состояние.
Как вы увидите, компоненты Post
и List
не имеют состояния. Они просто тупые компоненты. Родительский компонент Posts
отображает их.
Вы можете увидеть рабочий пример здесь , раскошелиться и проиграть его. Для этого примера есть отдельные каталоги и файлы. Я просто положил все в один файл, чтобы переместить его сюда правильно. Я не могу обещать держать песочницу слишком долго, поэтому вы можете сразу же ее раскошелиться:)
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,
);