处理俩类数据

任务一:添加 React Router 进来

代码:add react router

src/components/App.js

import React, { Component } from 'react';
 -import PostBody from './PostBody';
 -import CommentBox from './CommentBox';
 -import { Provider } from 'react-redux';
 -import store from '../store';
 +

  class App extends Component {
    render(){
      return(
 -      <Provider store={store}>
 -        <div>
 -          <div className="top  clearfix">
 -            <PostBody />
 -          </div>
 -          <div className="bottom clearfix">
 -            <CommentBox />
 -          </div>
 -        </div>
 -      </Provider>
 +      <div>
 +        { this.props.children }
 +      </div>
      )
    }
  }

src/components/CommentBox.js

import React, { Component } from 'react';
 -
  import store from '../store';

  class CommentBox extends Component {

src/components/Home.js

+import React, { Component } from 'react';
 +
 +class Home extends Component {
 +  render(){
 +    return(
 +      <div>
 +        HOME
 +      </div>
 +    )
 +  }
 +}
 +
 +export default Home;

src/components/Post.js

+import React, { Component } from 'react';
 +import PostBody from './PostBody';
 +import CommentBox from './CommentBox';
 +import { Provider } from 'react-redux';
 +import store from '../store';
 +
 +class Post extends Component {
 +  render(){
 +    return(
 +      <Provider store={store}>
 +        <div>
 +          <div className="top  clearfix">
 +            <PostBody />
 +          </div>
 +          <div className="bottom clearfix">
 +            <CommentBox />
 +          </div>
 +        </div>
 +      </Provider>
 +    )
 +  }
 +}
 +
 +export default Post;

src/index.js

import React, { Component } from 'react';
  import './main.css';
  import App from './components/App';
 +import Home from './components/Home';
 +import Post from './components/Post';
 +import { Router, Route, IndexRoute, browserHistory} from 'react-router';

 +const router = (
 +   <Router history={browserHistory}>
 +     <Route path="/" component={App}>
 +       <IndexRoute component={Home} />
 +       <Route path="/posts/:postId" component={Post} />
 +     </Route>
 +   </Router>
 +)

 -ReactDOM.render(<App />, document.getElementById('app'));
 +
 +ReactDOM.render(router, document.getElementById('app'));

src/store.js

import { createStore } from 'redux';

 -
  let comments = [
 -    "hello1",
 -    "hello2"
 +  "hello1",
 +  "hello2"
  ]

  function commentReducer(state = [], action) {
 @@ -17,6 +16,6 @@ function commentReducer(state = [], action) {
    }
  }

 -let store = createStore(commentReducer, comments);
 +const store = createStore(commentReducer, comments);

  export default store;

这部分没有什么新知识点引入。

任务二:添加多个 store 数据,添加 root reducer

代码:add rootReducer

src/components/CommentBox.js

constructor(){
      super();
      this.state = {
 -      comments: store.getState()
 +      comments: store.getState().comments
      }
    }
    handleSubmit(e){
      store.dispatch({type: 'ADD_COMMENT', comment: this.refs.comment.value});
      console.log(store.getState());
      this.refs.commentForm.reset();
 -    this.setState({comments: store.getState()});
 +    this.setState({comments: store.getState().comments});
    }
    render(){
      let commentList = this.state.comments.map((comment, i)=>{

src/components/Post.js

<Provider store={store}>
         <div>
           <div className="top  clearfix">
-            <PostBody />
+            <PostBody id={ this.props.params.postId } />
           </div>
           <div className="bottom clearfix">
             <CommentBox />
           </div>
         </div>
</Provider>

src/components/PostBody.js

return(
       <div className="post-body">
         <div className="comment-num">
-           { this.props.comments.length }
+           { this.props.comments.length }---
+           { this.props.id }
         </div>
       </div>
     )
   }
 }

 const mapStateToProps = (state) => ({
-  comments: state
+  comments: state.comments
 });

 export default connect(mapStateToProps)(PostBody);

src/reducers/comments.js

+function commentReducer(state = [], action) {
 +  // console.log(state, action);
 +  switch (action.type) {
 +    case 'ADD_COMMENT':
 +      // console.log([...state, action.comment])
 +      return [...state, action.comment]
 +    default:
 +      return state;
 +  }
 +}
 +
 +export default commentReducer;

src/reducers/index.js

+import { combineReducers } from 'redux';
 +
 +import postReducer from './posts';
 +import commentReducer from './comments';
 +
 +const rootReducer = combineReducers({
 +  posts: postReducer,
 +  comments: commentReducer
 +});
 +
 +export default rootReducer;

src/reducers/posts.js

+function postReducer(state = [], action) {
 +  return state
 +}
 +
 +export default postReducer;

src/store.js

import { createStore } from 'redux';
+import rootReducer from './reducers';
+

 let comments = [
   "hello1",
   "hello2"
 ]

-function commentReducer(state = [], action) {
-  // console.log(state, action);
-  switch (action.type) {
-    case 'ADD_COMMENT':
-      // console.log([...state, action.comment])
-      return [...state, action.comment]
-    default:
-      return state;
+const posts = [
+  {
+    id: 1,
+    title: 'redux-hello',
+    likes: 3
+  },
+  {
+    id: 2,
+    title: 'redux-baby',
+    likes: 6
   }
+]
+
+const defaultState = {
+  posts,
+  comments
 }

-const store = createStore(commentReducer, comments);
+const store = createStore(rootReducer, defaultState);

 export default store;
const rootReducer = combineReducers({
   posts: postReducer,
   comments: commentReducer
});

通过如上的 rootReducer 的使用,相当于对整个状态树进行的分拆。由 postReducer 专门更新 posts,由

commentReducer 专门负责 comments 数据。

上面的代码中把每篇 post (文章)也都接收到了数据自己的 id 。方便后续我们对不同文章进行区分,也对属于不同文

章的评论进行分组。

任务三:添加另一组评论,每篇 post 显示自己的评论

代码:show comments for each post

src/components/CommentBox.js

import store from '../store';

  class CommentBox extends Component {
 -  constructor(){
 -    super();
 +  constructor(props){
 +    super(props);
      this.state = {
 -      comments: store.getState().comments
 +      comments: store.getState().comments[this.props.postId]
      }
    }
    handleSubmit(e){
 @@ -14,7 +14,7 @@ class CommentBox extends Component {
      store.dispatch({type: 'ADD_COMMENT', comment: this.refs.comment.value});
      console.log(store.getState());
      this.refs.commentForm.reset();
 -    this.setState({comments: store.getState().comments});
 +    this.setState({comments:store.getState().comments[this.props.postId]});
    }
    render(){
      let commentList = this.state.comments.map((comment, i)=>{

src/components/Post.js

<Provider store={store}>
        <div>
          <div className="top  clearfix">
-            <PostBody id={ this.props.params.postId } />
+            <PostBody postId={ this.props.params.postId } />
          </div>
          <div className="bottom clearfix">
-            <CommentBox />
+            <CommentBox postId={this.props.params.postId} />
          </div>
        </div>
      </Provider>

src/components/PostBody.js

return(
        <div className="post-body">
          <div className="comment-num">
 -           { this.props.comments.length }---
 -           { this.props.id }
 +          { this.props.comments[this.props.postId].length }
          </div>
        </div>
      )

src/store.js

import rootReducer from './reducers';


 -let comments = [
 -  "hello1",
 -  "hello2"
 -]
 +let comments = {
 +  1: ['nice course', 'help me a lot'],
 +  2: ['really good', 'save me lots of time']
 +}

  const posts = [
{

任务四:提交评论失败,现在修复一下

代码: ADD_COMMENTS works

用到了对象展开运算符 。

src/components/CommentBox.js

handleSubmit(e){
      e.preventDefault();
      console.log(store.getState());
 -    store.dispatch({type: 'ADD_COMMENT', comment: this.refs.comment.value});
 +    store.dispatch({type: 'ADD_COMMENT', comment: this.refs.comment.value, postId: this.props.postId});
      console.log(store.getState());
      this.refs.commentForm.reset();
      this.setState({comments:store.getState().comments[this.props.postId]});

src/reducers/comments.js

-function commentReducer(state = [], action) {
 -  // console.log(state, action);
 -  switch (action.type) {
 +function postComments(state = [], action) {
 +  switch(action.type){
      case 'ADD_COMMENT':
 -      // console.log([...state, action.comment])
 -      return [...state, action.comment]
 +      return [...state, action.comment];
      default:
        return state;
    }
  }

 +function commentReducer(state = [], action) {
 +  if(typeof action.postId !== 'undefined') {
 +    return {
 +      ...state,
 +      [action.postId]: postComments(state[action.postId], action)
 +    }
 +  }
 +  return state;
 +}
 +
  export default commentReducer;

任务五:添加点赞功能

第一步,添加样式:like button style

第二步,从 store 中读取数据 read like number from store

src/components/PostBody.js

render(){
      return(
        <div className="post-body">
 -      <div className="likes-num num">
 -        23 喜欢
 -      </div>
 +        <div className="likes-num num">
 +          { this.props.posts[this.props.postId - 1].likes } 喜欢
 +        </div>
          <div className="comment-num num">
            { this.props.comments[this.props.postId].length } 评论
          </div>
 @@ -18,7 +18,8 @@ class PostBody extends Component {
  }

  const mapStateToProps = (state) => ({
 -  comments: state.comments
 +  comments: state.comments,
 +  posts: state.posts
  });

  export default connect(mapStateToProps)(PostBody);

第三步,添加 reducer 代码在INCREMENT_LIKES works

src/components/PostBody.js

import React, { Component } from 'react';
  import { connect } from 'react-redux';
 +import store from '../store';


  class PostBody extends Component {
 +  handleClick(){
 +    store.dispatch({type: 'INCREMENT_LIKES', index: this.props.postId - 1})
 +  }
    render(){
      return(
        <div className="post-body">
 -        <div className="likes-num num">
 +        <div className="likes-num num" onClick={this.handleClick.bind(this)}>
            { this.props.posts[this.props.postId - 1].likes } 喜欢
          </div>
          <div className="comment-num num">

src/reducers/posts.js

function postReducer(state = [], action) {
-  return state
+  switch(action.type) {
+    case 'INCREMENT_LIKES':
+      const i = action.index;
+      return [
+        ...state.slice(0, i),
+        {...state[i], likes: state[i].likes + 1},
+        ...state.slice(i + 1),
+      ]
+    default:
+      return state;
+  }
 }

 export default postReducer;

任务六:完成首页列表

第一步:PostBody 中添加 title add title to PostBody

src/components/PostBody.js

render(){
      return(
        <div className="post-body">
 +        <div className="title">
 +          { this.props.posts[this.props.postId - 1].title }
 +        </div>
          <div className="likes-num num" onClick={this.handleClick.bind(this)}>
            { this.props.posts[this.props.postId - 1].likes } 喜欢
          </div>
 +
          <div className="comment-num num">
            { this.props.comments[this.props.postId].length } 评论
          </div>

第二步:首页组件从 store 中读取数据 read post data from store

src/components/App.js

import React, { Component } from 'react';
 -
 +import { Provider } from 'react-redux';
 +import store from '../store';

  class App extends Component {
    render(){
      return(
 -      <div>
 -        { this.props.children }
 -      </div>
 +      <Provider store={store}>
 +        <div>
 +          { this.props.children }
 +        </div>
 +      </Provider>
      )
    }
  }
View  

src/components/Home.js

import React, { Component } from 'react';
 +import { connect } from 'react-redux';

  class Home extends Component {
    render(){
      return(
        <div>
 -        HOME
 +        {console.log(this.props.posts)}
        </div>
      )
    }
  }

 -export default Home;
 +const mapStateToProps = (state) => (
 +  {
 +    posts: state.posts
 +  }
 +)
 +
 +export default connect(mapStateToProps)(Home);

src/components/Post.js

class Post extends Component {
    render(){
      return(
 -      <Provider store={store}>
          <div>
            <div className="top  clearfix">
              <PostBody postId={ this.props.params.postId } />
 @@ -16,7 +15,6 @@ class Post extends Component {
              <CommentBox postId={this.props.params.postId} />
            </div>
          </div>
 -      </Provider>
      )
    }
  }

第三步:首页展示博客列表: add postList to HOME

src/components/App.js

import React, { Component } from 'react';
  import { Provider } from 'react-redux';
  import store from '../store';
 +import { Link } from 'react-router';

  class App extends Component {
    render(){
      return(
        <Provider store={store}>
          <div>
 +          <Link to='/'  className="back-home">HOME</Link >
            { this.props.children }
          </div>
        </Provider>

src/components/Home.js

import React, { Component } from 'react';
  import { connect } from 'react-redux';
 +import PostBody from './PostBody';

  class Home extends Component {
    render(){
 +    let postList = this.props.posts.map((post, i) => <PostBody postId={post.id} key={i}>{post.title}</PostBody> )
      return(
 -      <div>
 -        {console.log(this.props.posts)}
 +      <div className='home'>
 +        {postList}
        </div>
      )
    }

src/components/PostBody.js

import React, { Component } from 'react';
  import { connect } from 'react-redux';
  import store from '../store';
 +import { Link } from 'react-router';


  class PostBody extends Component {
 @@ -10,9 +11,9 @@ class PostBody extends Component {
    render(){
      return(
        <div className="post-body">
 -        <div className="title">
 +        <Link to={`/posts/${this.props.postId}`} className="title">
            { this.props.posts[this.props.postId - 1].title }
 -        </div>
 +        </Link>
          <div className="likes-num num" onClick={this.handleClick.bind(this)}>
            { this.props.posts[this.props.postId - 1].likes } 喜欢
          </div>