Grab and display single JSON object in React-Redux Code Answer

Hello Developer, Hope you guys are doing great. Today at Tutorial Guruji Official website, we are sharing the answer of Grab and display single JSON object in React-Redux without wasting too much if your time.

The question is published on by Tutorial Guruji team.

I did a tutorial and am able to display all of my Articles objects but I cannot figure out how to modify the code in order to grab one specific object and store it in the state. I have tried a lot of different things but I keep getting ‘TypeError: Cannot read property ‘name’ of undefined’.

One note, the id that I am looking for is stored in ‘this.props.match.params.id’ but I don’t really know what this means or how to use it. Thanks

ArticleShow.js

import React, { Component } from "react";
import { Container } from "reactstrap";
import { connect } from "react-redux";
import { getArticle } from "../actions/articleActions";
import PropTypes from "prop-types";

class articleShow extends Component {
  componentDidMount() {
    this.props.getArticle();
  }

  render() {
    const { article } = this.props.article;


    return (
      <Container>
        {article.name}
        <br />
        {article.author}
        <br />
        {article.body.split("r").map((c) => {
          return <p> {c} </p>;
        })}
        <br />
      </Container>
    );
  }
}

ArticleShow.propTypes = {
  getArticle: PropTypes.func.isRequired,
  article: PropTypes.object.isRequired,
};

const mapStateToProps = (state, props) => ({
  article: state.article,
});

export default connect(mapStateToProps, { getArticle })(ArticleShow);

articleActions.js

import axios from "axios";

import {
  GET_ARTICLES,
  GET_ARTICLE,
} from "./types";

export const getArticles = () => (dispatch) => {
  dispatch(setArticlesLoading());
  axios.get("/api/articles").then((res) =>
    dispatch({
      type: GET_ARTICLES,
      payload: res.data,
    })
  );
};

export const getArticle = (id) => (dispatch) => {
  dispatch(setArticlesLoading());
  axios.get(`/api/articles/${id}`).then((res) =>
    dispatch({
      type: GET_ARTICLE,
      payload: res.data,
    })
  );
};

articleReducer.js

import {
  GET_ARTICLES,
  GET_ARTICLE,
} from "../actions/types";

const intialState = {
  articles: [],
  loading: false,
};

export default function (state = intialState, action) {
  switch (action.type) {
    case GET_ARTICLES:
      return {
        ...state,
        articles: action.payload,
        loading: false,
      };
    case GET_ARTICLE:
      return {
        ...state,
        article: action.payload,
        loading: false,
      };
  default:
      return state;
  }
}

routes/api/articles.js

const express = require("express");
const router = express.Router();

// Article Model
const Article = require("../../models/Article");

router.get("/", (req, res) => {
  Article.find()
    .sort({ date: -1 })
    .then((articles) => res.json(articles));
});

router.get("/:id", (req, res) => {
  Article.findById(req.params.id).then((article) => res.json(article));
});

module.exports = router;

models/Article.js

const mongoose = require("mongoose");
const Schema = mongoose.Schema;

// create schema
const ArticleSchema = new Schema({
  name: {
    type: String,
    required: true,
  },
  author: {
    type: String,
    required: true,
  },
  body: {
    type: String,
    required: true,
  },
  date: {
    type: Date,
    default: Date.now,
  },
});

module.exports = Article = mongoose.model("article", ArticleSchema);

store.js

import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";

const initialState = {};

const middleware = [thunk];

const store = createStore(
  rootReducer,
  initialState,
  compose(
    applyMiddleware(...middleware),
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
  )
);

export default store;

Answer

First, if possible I would suggest you to reconsider how you are storing your data in articles node. In articles array, it would save you lots of complications if you store articles as object instead of array with article id as key if we are updating/deleting/accessing these articles Secondly, Article should be child component of Articles which would ensure a particular article would always exists when an Article component loads

articles: {
  5f0b628f172467147fbed0c2: {
    "name":"Article 4",
    "author":"Carol Henderson"
  }
}

In that scenario, your switch block would look like this:

case GET_ARTICLES:
      return {
        ...state,
        articles: action.payload.reduce((accObj, curObj) => {...accObj, [curObj._id]: curObj}, {}),
        loading: false,
      };
case GET_ARTICLE:
      return {
        ...state,
        articles: {
          [action.payload._id]: action.payload,
        },
        loading: false,
      };

But still if you keep it in your current shape due to some use case, you could try this:

case GET_ARTICLE:
      // find the article and merge more details
      const article = state.articles.find((art) => art._id === action.payload._id);
      article = {...article, ...action.payload};
      // since state has reference of article via references, your state has now new values.
      return {
        ...state,
        loading: false,
      };

I haven’t tested the code. But it should give you an idea how to go about it

We are here to answer your question about Grab and display single JSON object in React-Redux - If you find the proper solution, please don't forgot to share this with your team members.

Related Posts

Tutorial Guruji