better way to return states within nested objects

2019-08-26 22:27发布

问题:

I'm retrieving data from a backend, the data structure is like this.

Data Strucuture

{
  "id": 154,
  "image_title": "iiisdd",
  "img_url": "*********",
  "created_at": "2019-07-18T19:44:49.805Z",
  "updated_at": "2019-07-18T19:44:49.805Z",
  "user_id": 1,
  "user": {
    "id": 1,
    "googleId": null,
    "username": "*******,
    "password": "$********",
    "email": "e*******",
    "created_at": "2019-06-23T18:57:17.253Z",
    "updated_at": "2019-06-23T18:57:17.253Z"
  },
  "comments": [
    {
      "id": 51,
      "comment_body": "owls life",
      "created_at": "2019-07-18T20:04:51.484Z",
      "updated_at": "2019-07-18T20:04:51.484Z",
      "user_id": 8,
      "image_id": 154,
      "user": {
        "id": 8,
        "googleId": null,
        "username": "guest",
        "password": "********u",
        "email": "*******m",
        "created_at": "2019-07-18T20:04:34.315Z",
        "updated_at": "2019-07-18T20:04:34.315Z"
      }
    },
    {
      "id": 52,
      "comment_body": "dadad",
      "created_at": "2019-07-19T20:16:40.103Z",
      "updated_at": "2019-07-19T20:16:40.103Z",
      "user_id": 1,
      "image_id": 154,
      "user": {
        "id": 1,
        "googleId": null,
        "username": "*******",
        "password": "*********",
        "email": "el***********",
        "created_at": "2019-06-23T18:57:17.253Z",
        "updated_at": "2019-06-23T18:57:17.253Z"
      }
    },
    {
      "id": 53,
      "comment_body": "test",
      "created_at": "2019-07-21T22:12:44.729Z",
      "updated_at": "2019-07-21T22:12:44.729Z",
      "user_id": 1,
      "image_id": 154,
      "user": {
        "id": 1,
        "googleId": null,
        "username": "********",
        "password": "*********",
        "email": "el********",
        "created_at": "2019-06-23T18:57:17.253Z",
        "updated_at": "2019-06-23T18:57:17.253Z"
      }
    }
  ],
  "likes": [
    {
      "id": 24,
      "user_id": 2,
      "image_id": 154,
      "created_at": "2019-07-22T19:26:27.034Z",
      "deleted_at": "2019-07-22T19:26:27.034Z",
      "restored_at": "2019-07-22T19:26:27.034Z",
      "updated_at": "2019-07-22T19:26:27.034Z"
    },
    {
      "id": 141,
      "user_id": 1,
      "image_id": 154,
      "created_at": "2019-07-23T19:57:08.178Z",
      "deleted_at": "2019-07-23T19:57:08.178Z",
      "restored_at": "2019-07-23T19:57:08.178Z",
      "updated_at": "2019-07-23T19:57:08.178Z"
    }
  ]
}

I don't think im passing the data in reducer quite efficiently. What is a better way to refactor this code so that i can easily map the data on the component/ui.

Without having to do {this.props.image.images.comments} or {this.props.images.likes} etc.

I want to implement this reducer example

https://github.com/hibiken/hackafy/blob/master/src/reducers/posts.js

but unsure if i really need too.

Reducer

import {
  UPLOAD_IMAGE_SUCCESS,
  POST_COMMENT_SUCCESS,
  DELETE_IMAGE_FAILURE,
  FETCH_IMAGES_SUCCESS,
  DISLIKE_POST_SUCCESS,
  POST_COMMENT,
  POST_LIKE,
  POST_LIKE_SUCCESS,
  POST_LIKE_FAILURE,
  DELETE_IMAGE_SUCCESS,
} from '../actions/types';
const initialState = {
  images: [],
  likedByuser: false,
};
export default (state = initialState, action) => {
  switch (action.type) {
    case FETCH_IMAGES_SUCCESS:
      return {
        ...state,
        images: action.images,
      };
    case UPLOAD_IMAGE_SUCCESS:
      const newImage = action.data;
      return {
        images: [
          {
            id: newImage[0].id,
            user: {
              username: newImage[0].user.username,
            },
            comments: {
              comment_body: newImage[0].comments.comment_body,
            },
            image_title: newImage[0].image_title,
            img_url: newImage[0].img_url,
          },
          ...state.images, // pass the previous images,
        ],
      };
    case DELETE_IMAGE_SUCCESS:
      // console.log(action)
      return {
        ...state,
        images: state.images.filter(img => img.id !== action.data),
      };
    case DELETE_IMAGE_FAILURE:
      return {
        ...state,
        error: action.error,
      };
    case POST_LIKE:
      console.log(action);
      return {
        ...state,
      };
    case POST_LIKE_SUCCESS:
      console.log(action.data);
      const newState = { ...state }; // here I am trying to shallow  copy the existing state;
      const existingLikesOfPost = newState.images.find(image => image.id === action.data).likes;
      console.log(existingLikesOfPost)
      newState.images.find(image => image.id === action.data).likes = [...existingLikesOfPost, action.newLikeObject]; // using this approach I got some code duplication so I suggested the first approach of using **push** method of array.
      return newState;
    case DISLIKE_POST_SUCCESS:
      // ....
    case POST_COMMENT:
      return {
        ...state,
      };
    case POST_COMMENT_SUCCESS:
      //  adds a comment to a post without having to re render.
      // console.log(action.data.commentBody);
      return {
        ...state,
        images: state.images.map((image) => {
          // appends new comment withing images redux state. only if image.id === action.id
          if (image.id === action.id) {
            return {
              ...image,
              comments: [
                ...image.comments,
                {
                  comment_body: action.data[0].comment_body,
                  user: {
                    username: action.data[0].user.username,
                  },
                },
              ],
            };
          }
          return image;
        }),
      };
    default:
      return state;
  }
};

回答1:

You should normalize your state as explained in the redux docs. The key points:

  1. You should keep your store as flat as possible
  2. Try to reduce duplicate information => only set ids references instead of actual data
  3. don't use arrays to store your data, because you will have to use find or filter a lot, instead use a map/object with the key as the access point to the actual object like this:

    comments: {comment_id1: {title: 'title', author:'author_id'}}

to get all comments, add a allComments array with only the ids like this

allComments: ['comment_id1']

You should use a new reducer for each of these subsets of data. If you have more questions, let me know or read the docs.