Access element inside response as object - javascript

I have a response from my mongodb database as the following
{_id: '61ca4273e7cc1da1f3dbc9a3', title: 'Hero Syndrome', slug: 'hero-syndrome', category: 'Game', release_date: null, … }
I'm using Redux to fetch the data.
When I do console.log(game) which is the Object I provided, the console return the Object indeed. But when I'm trying to access the children such as title or slug it doesn't work.
I used to have this error Objects are not valid as a React child .. but fixed it somehow randomly in the code.
Any idea how to access title for example ?
What I tried : {title}, title, {game.title} and none of them work
What I did to get data from Redux :
GameComponent.propTypes = {
game: PropTypes.object.isRequired,
};
const mapStateToProps = state => ({
game: state.game,
});
And at the top of the component
function GameComponent({
game: { game, loading, title },
}) { ....
I tried to convert the object to string and to array in order for React to read it but I failed.
Code :
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getGameByToken } from '../actions/game';
import GameOverview from './GameOverview';
function GameComponent({ game: { game, loading, title }, getGameByToken, auth }) {
useEffect(() => {
getGameByToken(token);
}, [getGameByToken, token]);
return <>
//this doesn't work
Title : {title}
</>;
}
GameComponent.propTypes = {
getGameByToken: PropTypes.func.isRequired,
game: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
game: state.game,
auth: state.auth,
});
export default connect(mapStateToProps, { getGameByToken })(GameComponent);
Response from database in redux :
import axios from 'axios';
import { setAlert } from './alert';
import { GET_GAME, GAME_ERROR } from './types';
// GET GAMES BY TOKEN
export const getgameByToken = (token) => async (dispatch) => {
try {
const res = await axios.get('/api/games/' + token);
dispatch({ type: GET_GAME, payload: res.data });
} catch (err) {
dispatch({
type: GAME_ERROR,
payload: {
msg: err.response.msg,
status: err.response.status,
},
});
}
};
From Redux Dev Tools :
EDIT: If I rename game: state.game, to game: state.game.game, I actually got the value ! But when refreshing It goes back saying TypeError: Cannot read properties of null (reading 'title')

Related

Still getting undefined to a property in node with react project

I have three different components that within them are using a component called StripeCheckout and one of the properties of StripeCheckout is description which I currently have as a string:
import React, { Component } from "react";
import StripeCheckout from "react-stripe-checkout";
import { connect } from "react-redux";
import * as actions from "../actions";
class SunnySampler extends Component {
render() {
return (
<div>
<StripeCheckout
name='Microurb Farms'
amount={this.props.amount}
description='Sunny Sampler Box'
shippingAddress
billingAddress={false}
zipCode={true}
token={(token, amount) =>
this.props.handleToken(token, this.props.amount)
}
stripeKey={process.env.REACT_APP_STRIPE_KEY}
/>
</div>
);
}
}
export default connect(null, actions)(SunnySampler);
SunnySampler is just one of the three components making use of StripeCheckout. Each has its own amount property dynamically coded and passed down to the express api and yet I cannot seem to pass down the description property successfully.
The challenge also is that each description property is different depending on which component was selected.
So I was able to pass in the amount dynamically here:
const tiers = [
{
title: "Half pound boxes",
price: "10",
description: [
"Sunflower Shoots",
"Pea Shoots",
"Radish Shoots",
"Broccoli Shoots",
],
buttonText: <HalfPound amount={1000} />,
buttonVariant: "outlined",
},
{
title: "Grasses",
subheader: "Tray",
price: "15",
description: ["Wheatgrass", "Barleygrass"],
buttonText: <Grasses amount={1500} />,
buttonVariant: "contained",
},
{
title: "Sunny Sampler Box",
price: "20",
description: [
"6oz Sunflower",
"2oz Broccoli",
"3oz Sweet Pea",
"2oz Radish",
],
buttonText: <SunnySampler amount={2000} />,
buttonVariant: "outlined",
},
];
this is inside of Dashboard.js, then in my action creator I pass it in like so:
export const handleToken = (token, amount) => async (dispatch) => {
const res = await axios.post("/api/stripe", { token, amount });
dispatch({ type: FETCH_USER, payload: res.data });
};
Inside each of those payment type of components it looks like so:
import React, { Component } from "react";
import StripeCheckout from "react-stripe-checkout";
import { connect } from "react-redux";
import * as actions from "../actions";
class SunnySampler extends Component {
render() {
return (
<div>
<StripeCheckout
name='Microurb Farms'
amount={this.props.amount}
description='Sunny Sampler Box'
shippingAddress
billingAddress={false}
zipCode={true}
token={(token, amount) =>
this.props.handleToken(token, this.props.amount)
}
stripeKey={process.env.REACT_APP_STRIPE_KEY}
/>
</div>
);
}
}
export default connect(null, actions)(SunnySampler);
and finally my backend api:
const keys = require("../config/keys");
const stripe = require("stripe")(keys.stripeSecretKey);
module.exports = (app) => {
app.post("/api/stripe", async (req, res) => {
const { amount, token } = req.body;
// const description = req.body.data.description;
const charge = await stripe.charges.create({
amount: amount,
currency: "usd",
source: token.id,
});
console.log(charge);
});
};
I tried taking the same approach I took to the amount property with the description property and variations of it and I am still getting undefined.
Originally, inside the action creator I had passed in description to it and then inside the handleToken I had passed in this.props.description and then inside the api route on the backend I had req.body.description which should have worked, but I got undefined.
When I console log req.body I see in the data structure description: null, despite having passed a string into the description property inside of StripeCheckout component. I cannot explain why that is.

Object TypeError: Cannot read property 'title' of null

I am facing a bug that I can not resolve. I am building a blogging website as a side project. Right now I am trying to render a single post, but when I try to access object properties I get an error TypeError: Cannot read property 'title' of null. I don't understand why object properties are null, event though I can print the object itself. Here are the code snippets:
This is a PostView Component that will handle rendering of the post content. I can print in the console the post object that I receive from the api but when I try access or print its properties like title, body and etc... I get an error. At first I thought I had an error in redux reducers and actions but it seems it's works fine. The states are changing and and I receive the json response. I used similar code for my other components and it worked, so I don't understand where I am making the mistake here?
import React, {useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getPost} from 'actions/post';
//Components
import PostContent from '../Content/PostContent';
//Material Ui
import { Grid } from '#material-ui/core';
import { useStyles } from '#material-ui/core/styles';
const PostView = ({ getPost, post: {post: { title, body, category }, loading }, match}) => {
useEffect(() => {
getPost(match.params.id);
},[getPost]);
//This code works
console.log(post);
//But this one does not
console.log(post.title);
return (
<Grid container>
<PostContent/>
</Grid>
)
}
PostView.propTypes = {
getPost: PropTypes.func.isRequired,
post: PropTypes.object.isRequired,
}
const mapStateToProps = (state) => ({
post: state.post
});
export default connect(mapStateToProps, { getPost })(PostView)
Here is also my actions function:
//Get Post by Id
export const getPost = (id) => async dispatch => {
try {
const res = await axios.get(`/api/posts/${id}`);
dispatch({
type: GET_POST,
payload: res.data
});
}catch(err){
dispatch({
type: POST_ERROR,
payload: {msg: err.response.statusText, status: err.response.status}
});
}
};
And the post reducer file:
import{
GET_POSTS,
POST_ERROR,
UPDATE_VOTES,
ADD_POST,
GET_POST,
GET_POSTS_IMAGE,
POSTS_IMAGE_ERROR
} from '../actions/types';
const initialState = {
posts: [],
post: null,
loading: true,
status: true,
error: {}
}
export default function(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case GET_POSTS:
return{
...state,
posts: payload,
loading: false
};
case POST_ERROR:
return{
...state,
error: payload,
loading: false
} ;
case UPDATE_VOTES:
return{
...state,
posts: state.posts.map(post => post._id === payload.postId ? { ...post, upVotes: payload.upVotes} : post),
loading: false
};
case ADD_POST:
return{
...state,
posts: [...state.posts, payload],
loading: false,
status: false
};
case GET_POST:
return{
...state,
post: payload,
loading: false
}
default:
return state;
}
}
I am only learning react and redux, so any help will be appreciated.strong text
The error TypeError: Cannot read property 'title' of null. indicates that at some point, you are reading a property from a value of null, which will cause an error.
From your code, you are setting the initial state of post to null in the reducer, as a result, there is a moment where the prop post is null, the error originates from this moment.
post only updates to a non null value (assuming the ajax call will response with some data), and can be safely accessed after the getPost is finished.
This probably happens because your Redux initial state of post is null and you are trying to read some property of null.
Try to add some conditionals, like:
if(post && post.title) {
console.log(post.title)
}
Or change your Redux initial state :
const initialState = {
posts: [],
post: {
title: "",
body: "",
...etc
},
loading: true,
status: true,
error: {}
}

Cannot access a nested array within an object in react-redux

Hello, I am new to redux and I am struggling with a problem. I am trying to access and map over the comments within my post array. However, I am not sure how to do this. So far, I've tried changing the actions and reducers in order to solve this issue. I think the problem is within the react and redux. I can't tell if my mapStateToProps is working correctly. Also, the state is being fetched from my express server and it seems to be working properly as you can see in the picture.
My getPost action:
export const getPost = (group_id, post_id) => async dispatch => {
try {
const res = await axios.get(`/api/groups/${group_id}/${post_id}`);
dispatch({
type: GET_POST,
payload: res.data
});
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.statusText, status: error.response.status }
});
}
};
The initial state:
const initialState = {
groups: [],
group: [],
loading: true,
error: {}
};
The reducer:
case GET_POST:
return {
...state,
post: payload,
loading: false
};
Where I'm trying to map over the comments:
import React, { Fragment, useEffect } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getPost } from '../../../redux/actions/group';
const Post = ({ getPost, post, match }) => {
useEffect(() => {
getPost(match.params.group_id, match.params.post_id);
}, [getPost, match.params.group_id, match.params.post_id]);
// I want to map over the comments here
return (
{post.comments.map(comment => ({ comment }))}
);
};
Post.propTypes = {
getPost: PropTypes.func.isRequired,
group: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
post: state.post
});
export default connect(mapStateToProps, { getPost })(Post);
You can access nested object with some tricks using redux, we have use this way in our prod env for some time.
First the reducer (you can make this reducer even more complex)
const LocalStorageReducer = createReducer<Store['localStorage']>(
new LocalStorage(),
{
saveLocalStorageItem(state: LocalStorage, action: any) {
return {...state, [action.payload.item]: action.payload.value}; // <= here
},
}
);
For Actions
export const actions = {
saveLocalStorageItem: (payload: InputAction) => ({type: 'saveLocalStorageItem', payload}),
};
For the type InputAction
export class InputAction {
item: string;
value: string | Array<string> | null | boolean;
constructor() {
this.item = '';
this.value = null;
}
}
For the handler in component
this.props.saveLocalStorage({ item: 'loading', value: false });
In this way you can go one way done to the nested redux store.
For complex (4-5 levels) and multiple (> 2 times) data structure, there are other ways, but in most situations, it's good enough.

How to delete certain properties from an object in an axios response in redux while calling an API server

I am calling an API server from my redux project where I want to extract the data.The data in the API is in the form as shown below:
const defaultData = {
categories: [
{
name: 'react',
path: 'react'
},
{
name: 'redux',
path: 'redux'
},
{
name: 'udacity',
path: 'udacity'
}
]
}
So, in my redux "Actions", I am using axios to make the API call.The actions file is given below:
import axios from 'axios';
export const FETCH_CATEGORIES = 'fetch_categories';
let token;
if (!token)
token = localStorage.token = Math.random().toString(32).substr(-8);
const API = 'http://localhost:3001';
const headers = {
'Accept' : 'application/json',
'Authorization' : 'token'
}
export function fetchCategories() {
const URL = `${API}/categories`;
const request = axios.get(URL,{headers});
return dispatch => {
return request.then((data) => {
dispatch({
type : FETCH_CATEGORIES,
payload : data
})
})
}
}
I am trying to save the result of the API call in the application state in my reducer.The Reducer for the categories looks like this:
import _ from 'lodash';
import { FETCH_CATEGORIES } from '../actions/categories_action';
export default function(state={}, action) {
switch(action.type) {
case FETCH_CATEGORIES:
return {categories: {...state.categories, ...action.payload}};
default:
return state;
}
}
And I am using combineReducers() to combine all the reducers in my index file as shown below:
import { combineReducers } from 'redux';
import PostReducer from './PostsReducer';
import CategoriesReducer from './CategoriesReducer';
const rootReducer = combineReducers({
loading: false,
posts: PostReducer,
categories: CategoriesReducer
});
export default rootReducer;
Then, in my component I am trying to show the data from the state.
So,when I try to console.log the value of the categories state, I get something like this as shown in the image below:
But I just want the categories property where I get the three categories(I want to omit the config,headers,request properties).
I even tried something like: console.log(this.props.categories.data.categories) ,but that gives me an undefined value.
Can anyone please help me with this?
That is because of this line {categories: {...state.categories, ...action.payload}};
Change that to {categories: [...state.categories, ...action.payload.data.categories]};

Error: name":"Invariant Violation","framesToPop":1

I'm seeing this strange error.
I'm writing an app which uses the graph api to retrieve event details from facebook.
The event has a couple of attributes from which:
- owner which is an object containing owner id, owner name, and other attributes
- cover which is an object representing the event cover image details.
I save events in a mongo database, here is what my event model looks like:
const EventSchema = new Schema({
title: String,
name: String,
_id: {
type: String,
unique: true,
default: shortid.generate,
},
start_time: Date,
end_time: Date,
description: String,
owner: {},
cover: {},
venue: {},
privacy: String,
timezone: String,
location: String,
createdAt: { type: Date, default: Date.now },
event_type: {},
});
I have an express route which sends back a given event by id:
router.get('/:id', (req, res) => {
Event.findById(req.params.id).exec((error, events) => {
if (error){
res.json(error);
}
res.json(events);
})
});
My component architecture goes like this:
-EventPage component which contains an EventDetails component.
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import axios from 'axios';
import EventDetails from './eventDetails';
class EventPage extends React.Component {
constructor(props) {
super(props);
this.state = {
event: {},
};
}
componentWillMount() {
axios.get(`/api/events/${this.props.params.id}`)
.then((eventResponse) => {
this.setState({
event: eventResponse.data
})
}).catch((err) => {
console.log(JSON.stringify(err));
})
}
render() {
return (
<div className="row">
<EventDetails event={this.state.event} />
</div>
)
}
}
EventPage.propTypes = {
};
export default EventPage;
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import _ from 'lodash';
class EventDetails extends React.Component {
constructor(props) {
super(props);
}
render() {
const { name, description, start_time, end_time, owner } = this.props.event;
return(
<div className='row'>
<h1>{name}</h1>
<p>{description}</p>
<p>{JSON.stringify(this.props.event)}</p>
<p>{this.props.event.owner}</p>
<pre>
</pre>
</div>
)
}
}
EventDetails.propTypes = {
};
export default EventDetails;
Trying to display the event owner's name results in this error:
{"name":"Invariant Violation","framesToPop":1}
The error comes from the axios error handler in the EventPage component.
Anyone sees what I've done wrong here?
Thanks for your help
I had probably the same problem with {"name":"Invariant Violation","framesToPop":1}.
I've passed a javascript object instead of an array and it worked for me.
Message.find({}).sort({'date': -1}).limit(50).exec().then( (doc, err) => {
console.log('found');
const messages = [];
doc.map( (item) => {
messages.push({data: item});
});
callback(err, {items: messages});
});

Categories