How to retrieve nested JSON objects in React-Redux? - javascript

I am still learning React-Redux. I understand how to retrieve simple JSON arrays/objects. However, I am not sure how to call a nested object. When this is bundled, I am viewing the 'after' object - t3_5t61fz - obviously because I'm calling data.after, but when I try to implement data.title nothing appears. I am trying to grab the title and am viewing this in the console:
Object
data
:
Object
data
:
Object
data
:
Object
after
:
"t3_5t61fz"
before
:
null
children
:
Array[25]
0
:
Object
data
:
Object
title
:
"How Google builds a web framework"
Reducer:
export default function reducer(state={
data: {
data: {}
},
fetching: false,
fetched: false,
error: null,
}, action) {
switch(action.type){
case "FETCH_DATA":{
return {...state, fetching:true}
}
case "FETCH_DATA_REJECTED":{
return {...state, fetching: false, error: action.payload}
}
case "FETCH_DATA_FULFILLED":{
return {...state, fetching: false, fetched: true, data: action.payload}
}
case "ADD_DATA":{
return {...state, data: [...state.data, action.payload]}
}
case "UPDATE_DATA":{
const { id, title } = action.payload
const newData = [...state.data]
const dataToUpdate = newData.findIndex(data => data.id === id)
newData[dataToUpdate] = action.payload;
return {...state, data: newData}
}
case "DELETE_DATA":{
return {...state, data: state.data.filter(data => data.id !== action.payload)}
}
}
return state
}
Action
import axios from 'axios';
export function fetchData(){
return function(dispatch){
axios.get("https://www.reddit.com/r/webdev/top/.json")
.then((response) => {
dispatch({ type: "FETCH_DATA_FULFILLED", payload: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_DATA_REJECTED", payload: err})
})
}
}
export function addData(id, text){
return {
type: 'ADD_DATA',
payload:{
id,
data,
},
}
}
export function updateData(id, text){
return {
type: 'UPDATE_DATA',
payload: {
id,
data,
},
}
}
export function deleteData(id){
return {
type: 'DELETE_DATA',
payload: id
}
}
Layout.js
import React from "react"
import { connect } from "react-redux"
import { fetchData } from "../actions/dataActions"
#connect((store) => {
return {
data: store.data.data
};
})
export default class Layout extends React.Component {
componentWillMount() {
this.props.dispatch(fetchData())
}
render() {
const { data } = this.props;
return <div>
<h1>{data.data.after}</h1>
</div>
}
}

I'm calling data.after, but when I try to implement data.title nothing appears
I see that data.after is an array, and you are trying to get the title of the data of the first item in that array.
You should be able to access the data in your <h1> by getting {data.data.after[0].data.title}
Im curious why the data is being so deeply nested in this fashion though.

Related

why the profile inside the state return null instead of empty object after update?

I have a problem with my redux reducer.It doesn't return the expected state after dispatching the getCurrentProfile action, it returns the initial state which is "null" instead of "{}", which is fetched with an ajax request, so when the network return the result the state profile change to the result returned but when it is an error returned it stay null instead of empty object, so that is my code:
enter image description here
profileAcction.js :
import axios from 'axios';
import { GET_PROFILE, PROFILE_LOADING, CLEAR_CURRENT_PROFILE} from './types';
//Loading profile
const setProfileLoading = () => {
return {
type: PROFILE_LOADING
}
};
// Clear current profile
export const clearCurrentProfile = () => {
return {
type: CLEAR_CURRENT_PROFILE,
}
}
// Get current profile
export const getCurrentProfile = () => dispatch => {
dispatch(setProfileLoading());
axios.get('/api/profile')
.then(res => dispatch({
type: GET_PROFILE,
payload: res.data
})).catch(error =>
dispatch({
type: GET_PROFILE,
payload: {}
}))
};
profileReducer.js:
import {GET_PROFILE, PROFILE_LOADING, CLEAR_CURRENT_PROFILE} from '../actions/types';
const initialState = {
profile: null,
profiles: null,
loading: false
};
const profileReducer = (state=initialState, action) => {
switch(action.type) {
case PROFILE_LOADING:
return {
...state,
loading: true
}
case GET_PROFILE:
return {
...state,
profile: action.payload,
loading: false
}
case CLEAR_CURRENT_PROFILE:
return {
...state,
profile: null,
loading: false
}
default:
return state;
}
};
export default profileReducer;

Redux: altering different parts of the initial state in Reducer according to Actions

I have the following Reducer:
const initialState = {}
const dishReducer = (state = initialState, action) => {
switch (action.type) {
case 'LOAD_DISHES':
return (action.dishes)
case 'LOAD_DISHES_ERROR':
console.log("load dishes error")
return state
case 'LOAD_DISHES_SUCCESS':
console.log("load dishes success")
return state
default:
return state;
}
};
export default dishReducer;
And the following action(s):
import {database} from '../../config/fbConfig'
export const startLoadingDishes = (dishes) => {
return (dispatch) =>{
return database.ref('products-dishes').once('value').then((snapshot) => {
let dishes = {}
snapshot.forEach((childSnapshot) => {
let parentkey = childSnapshot.key
let dishArray = [];
childSnapshot.forEach((dish) =>{
dishArray.push(dish.val())
});
dishes[childSnapshot.key] = dishArray;
})
dispatch(loadDishes(dishes))
}).then(() => {
dispatch({ type: 'LOAD_DISHES_SUCCESS' });
}).catch(err => {
dispatch({ type: 'LOAD_DISHES_ERROR' }, err);
});
}
}
export const loadDishes = (dishes) => {
return {
type: 'LOAD_DISHES',
dishes
}
}
The 'startLoadingDishes' action is called inside the componentDidLoad() of a certain Component. However, I want to alter the initial state of my dishReducer so that it includes additional information, as follows:
const initialState = {
value : {},
loaded: false,
loading: false,
error: false
}
So now 'action.dishes' returned by reducer [in 'LOAD_DISHES' case] should be put inside the 'value' part of the state, instead of it being the whole state. Also, the 'loaded' part of the state should be set to true if dishes have already been loaded earlier, and so on. I understand this is fairly simple but as I am new to React+Redux, I don't know how to alter the Action/Reducer codes properly (while keeping state immutability). Any help is appreciated.
I originally asked the question, here is how I solved it (not sure if this is the 'best' way though):
New reducer file:
const initialState = {
value : {},
loaded: false,
loading: false,
error: false
}
const dishReducer = (state = initialState, action) => {
switch (action.type) {
case 'LOAD_DISHES':
return {
value: action.dishes,
loading: !state.loading,
loaded: false, //this might need to be set to true
error: false
}
case 'LOAD_DISHES_ERROR':
console.log("load dishes error")
return {
...state, //or: state.value, as the other parts of state are being overwritten below
loaded: false,
loading: false,
error: true
}
case 'LOAD_DISHES_SUCCESS':
console.log("load dishes success")
return {
...state, //better: state.value
loaded: true,
loading: false,
error: false
}
default:
return state;
}
};
export default dishReducer;
No change in actions file.
Now, inside the 'Main' component, I was originally accessing the state as such:
class Main extends Component {
componentDidMount() {
this.props.startLoadingDishes();
}
render() {
return (
//some code
)
}
}
const mapStateToProps = (state) => {
return {
dishes: state.dishes //to access dishes: dishes.value
}
}
export default connect(mapStateToProps, actions)(Main)
The Main component code also stayed the same, with the difference that now I use 'dishes.value' instead of just 'dishes' to access the value of dishes from the state (and dishes.loaded for loaded, and so on). And now the action caller inside componentDidMount is as follows:
componentDidMount() {
if(!this.props.dishes.loaded){
this.props.startLoadingDishes();
console.log("loading dishes from database")
}
}

Redux: dispatch(...).then is not a function

I have such action:
import { GET, POST, PUT, REMOVE } from "../../Utils/Http";
export const FETCH_ARTICLES = "FETCH_ARTICLES";
export const FETCH_ARTICLES_SUCCESS = "FETCH_ARTICLES_SUCCESS";
export const FETCH_ARTICLES_FAILURE = "FETCH_ARTICLES_FAILURE";
export const RESET_ARTICLES = "RESET_ARTICLES";
export function fetchArticles() {
const request = GET("/articles");
return {
type: FETCH_ARTICLES,
payload: request
};
}
export function fetchArticlesSuccess(articles) {
return {
type: FETCH_ARTICLES_SUCCESS,
payload: articles
};
}
export function fetchArticlesFailure(error) {
return {
type: FETCH_ARTICLES_FAILURE,
payload: error
};
}
and reducer:
import {
FETCH_ARTICLES,
FETCH_ARTICLES_SUCCESS,
FETCH_ARTICLES_FAILURE,
RESET_ARTICLES
} from "../Actions/Article";
const INITIAL_STATE = {
articlesList: {
articles: { data: [], total: 0 },
error: null,
loading: false
},
newTractor: { article: null, error: null, loading: false },
activeTractor: { article: null, error: null, loading: false },
deletedTractor: { article: null, error: null, loading: false }
};
const reducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case FETCH_ARTICLES:
return {
...state,
articleList: { articles: {}, error: null, loading: true }
};
case FETCH_ARTICLES_SUCCESS:
return {
...state,
articleList: { articles: action.payload, error: null, loading: false }
};
case FETCH_ARTICLES_FAILURE:
return {
...state,
articleList: { articles: {}, error: action.payload, loading: false }
};
case RESET_ARTICLES:
return {
...state,
articleList: { articles: {}, error: null, loading: false }
};
default:
return state;
}
};
export default reducer;
And i try it to use this way in list component:
import React, { Component } from "react";
import { connect } from "react-redux";
import { isUndefined } from "lodash";
import {
fetchArticles,
fetchArticlesSuccess,
fetchArticlesFailure
} from "../../Store/Actions/Article";
class ArticleList extends Component {
componentDidMount() {
this.props.fetchArticles();
}
render() {
return <div className="ui segment" />;
}
}
const mapDispatchToProps = dispatch => {
return {
fetchArticles: () => {
dispatch(fetchArticles()).then(response => {
!response.error
? dispatch(fetchArticlesSuccess(response.payload.data))
: dispatch(fetchArticlesFailure(response.payload.data));
});
}
};
};
export default connect(null, mapDispatchToProps)(ArticleList);
also Http.js:
import axios from "axios";
const http = axios.create({
baseURL: process.env.BASE_API_URL
});
export const GET = (url, params) => {
return new Promise((resolve, reject) => {
http({
method: "get",
url,
params
})
.then(response => {
resolve(response);
})
.catch(err => {
console.log("GET err ", err);
reject(err);
});
});
};
...
But as result I get:
TypeError: dispatch is not a function in dispatch(fetchArticles()).then(response => {
What I do wrong?
Also how can i write this part:
fetchTractors()).then(response => {
!response.error
? dispatch(fetchTractorsSuccess(response.payload.data))
: dispatch(fetchTractorsFailure(response.payload.data));
}
in component class? is it possible? (not to move it to the mapDispatchToProps block)
i took some ideas from here: https://github.com/rajaraodv/react-redux-blog/
I can see many problems here:
const mapDispatchToProps = dispatch => {
return {
fetchArticles: () => {
dispatch(fetchArticles()).then(response => {
!response.error
? dispatch(fetchArticlesSuccess(response.payload.data))
: dispatch(fetchArticlesFailure(response.payload.data));
});
}
};
};
dispatch is a synchronous thing by default unless you have configured some middleware such as redux-thunk to handle functions. dispatch takes native object as an argument in normal scenario.
dispatch does not return a promise. So then can not be used,
connect takes first arguments as mapStateToProps and second argument as mapDispatchtoProps. There is also third argument which is not generally used. So I will not mention it for now.
4.you need to pass the actions creators through mapDispatchToProps like this:
import { bindActionCreators } from "redux"
const mapDispatchToProps = dispatch => bindActionCreators({
fetchArticles,
fetchArticlesSuccess,
fetchArticlesFailure,
}, dispatch)
The probles is here:
export default connect(mapDispatchToProps)(ArticleList);
First parameter should be mapStateToProps. But you actually can pass null:
export default connect(null, mapDispatchToProps)(ArticleList);
If someone encountered this problem while using ts + redux, the IDE prompted you that there is no then method, you can refer to this link

Redux dispatches an API call failure even though the network tab in devtools shows the API call received a status of 200

I am new to redux and I am having a hard time understanding how to connect the payload of my API call to my state.
Right now my action.js file looks like this:
import ApiService from '../../services/ApiService';
import { reset } from 'redux-form';
//actions
export const getStock = () => {
return {
type: 'GET_STOCK'
}
}
export const getStockPending = () => {
return {
type: 'GET_STOCK_PENDING'
}
}
export const getStockFulfilled = (stock) => {
return {
type: 'GET_STOCK_FULFILLED',
payload: stock
}
}
export const getStockRejected = () => {
return {
type: 'GET_STOCK_REJECTED'
}
}
// async function calls
export function fetchStocksWithRedux() {
const action_type = "GET_STOCK";
const stock = 'AAPL';
return (dispatch) => {
dispatch({type: `${action_type}_PENDING`});
return ApiService.get(`/search?query=${stock}`)
.then(([response, json]) =>{
if(response.status === 200){
dispatch(getStockFulfilled(json))
}
else{
dispatch(getStockRejected())
}
})
}
}
and my reducer.js file looks like this:
const initialState = {
inProgress: false,
stock: {},
stocks: ['NKE', 'AMZN', 'AAPL'],
error: {}
}
export default (state = initialState, action) => {
switch(action.type) {
case 'GET_STOCK_PENDING':
return {
...state,
inProgress: true,
error: false
}
case 'GET_STOCK_FULFILLED':
return {
...state,
stock: action.payload,
inProgress: false
}
case 'GET_STOCK_REJECTED':
return {
...state,
inProgress: false,
error: action.error
}
default:
return state;
}
}
When I go to call my method fetchStocksWithRedux in my component, the network tab in my dev tools shows a 200 status and the response I'm expecting, but the reducer dispatches the 'GET_STOCK_REJECTED' action, but the error hash is empty. What do you think is going wrong?
Here is my component, for reference:
import React, { Component } from 'react';
import { fetchStocksWithRedux } from '../../redux/modules/Stock/actions';
import { connect } from 'react-redux';
class Dashboard extends Component {
componentDidMount() {
this.props.fetchStocksWithRedux()
}
render() {
return (
<div className="uk-position-center">
</div>
)
}
}
export default connect(
state => ({
stocks: state.stocks,
stock: state.stock
})
, { fetchStocksWithRedux }
)(Dashboard);
Thanks. Any advice or guidance would be greatly appreciated!

displaying data from axios using react.js and redux

So I'm learning redux currently and I'm making an app that displays a list of articles. But I can't figured out why my data from my back end isn't showing up. I'm not sure where my error is that preventing my data from my backend from showing up? I know it not the setup of redux because I did simpler app to see if that was the problem and it wasn't so it has to do more with the action, reducers , and component. I would like to go farther eventually when there is more data in the database where it provides a link so it goes to another page that shows all the information about that article.
data from my node backend
[{"_id":"58c71df9f7e4e47f1fe17eeb","article":"words words","author":"Jason","date":"1/2/2014","title":"my article","__v":0}]
fashionActions.js
import axios from "axios";
export function fetchFashionArticle() {
return function(dispatch) {
axios.get("http://localhost:3000/api/fashion")
.then((response) => {
dispatch({type: "FETCH_FASHIONARTICLES_FULFILLED", payload: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_FASHIONARTICLES_REJECTED", payload: err})
})
}
}
export function addFashionArticle(_id, title,date, author, article) {
return {
type: "ADD_FASHIONARTICLE",
payload: {
_id,
title,
date,
author,
article,
},
}
}
export function updateFashionArticle(_id, title,date, author, article) {
return {
type: "UPDATE_FASHIONARTICLE",
payload: {
_id,
title,
date,
author,
article,
},
}
}
export function deleteFashionArticle(id) {
return {type: 'DELETE_FASHIONARTICLE', payload: id}
}
FashionArticle.js
import React from "react";
import { connect } from "react-redux";
import {fetchFashionArticle} from "../actions/fashionActions";
#connect((store) => {
return {
fashionarticles:store.fashionarticles.fashionarticles,
};
})
export default class FashionArticle extends React.component {
fetchFashionArticle() {
this.props.dispatch(fetchFashionArticle())
}
render() {
const { fashionarticles } = this.props;
if(!fashionarticles.length) {
return <button onClick={this.fetchFashionArticles.bind(this)}>Load articles</button>
}
const mappedArticles = fashionarticles.map(fashionarticle => <li>{fashionarticle}</li>)
return(
<div>
<h1>Fashion Article</h1>
<h2>{fashionarticles.title}</h2>
</div>
)
}
}
fashionArticleReducers.js
export default function reducer(state={
fashionarticles: [],
fetching: false,
fetched: false,
error: null,
}, action) {
switch (action.type) {
case "FETCH_FASHIONARTICLES": {
return {...state, fetching: true}
}
case "FETCH_FASHIONARTICLES_REJECTED": {
return {...state, fetching: false, error: action.payload}
}
case "FETCH_FASHIONARTICLES_FULFILLED": {
return {
...state,
fetching: false,
fetched: true,
fashionarticles: action.payload,
}
}
case "ADD_FASHIONARTICLE": {
return {
...state,
fashionarticles: [...state.fashionarticles, action.payload],
}
}
case "UPDATE_FASHIONARTICLE": {
const { _id, title,date,author,article } = action.payload
const newFashionArticles = [...state.fashionarticles]
const fashionarticleToUpdate = newFashionArticles.findIndex(fashionarticle => fashionarticle.id === id)
newFashionArticles[fashionarticleToUpdate] = action.payload;
return {
...state,
fashionarticles: newFashionArticles,
}
}
case "DELETE_FASHIONARTICLE": {
return {
...state,
fashionarticles: state.fashionarticles.filter(fashionarticle => fashionarticle.id !== action.payload),
}
}
}
return state
}
index.js
import { combineReducers } from 'redux';
import user from './testReducers'
import fashionarticles from './fashionArticleReducers';
export default combineReducers({
user,
fashionarticles,
})
You're sending the payload with the axios response as type FETCH_FASHIONARTICLES_DONE but your reducer is listening for FETCH_FASHIONARTICLES_FULFILLED

Categories

Resources