Redux Store and nested JSON from Axios API

Multi tool use
Multi tool use


Redux Store and nested JSON from Axios API



I tried every possible variation of this code, but I don't really manage to get whatever the API fetched into my data store. I am absolutely stuck and would appreciate some help.
I think I just don't get the essential part of this construct and I would really like to understand how it works properly.



The data looks like this - it's basically a simple JSON (from a django restframework API) with some nested elements:



EDIT 2 (changed JSON to screenshot of axios API/ Redux action)
Data Types



My Redux action - works perfectly fine. console.log pulls exactly the data from above (with correct inputs) :


// ./action/plan.js

import axios from 'axios';

export function fetchBudgets(){
return function(dispatch){
axios.get("/api/budgets/")
.then((response) => {
console.log(response)
dispatch({ type: "FETCH_BUDGETS", budgets: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_DATA_REJECTED", budgets: err})
})
}
}



So until now, everything seems fine. The problems starts with the reducer - as I am not sure how to model the reducer to use the nested data.



My reducer:


// ./reducer/plan.js

const initialState = {}


export default function budgets(state=initialState, action) {


switch (action.type) {

case 'FETCH_BUDGETS':
console.log(action)
return {
...state,
id: action.budgets.id,
value_jan: action.budgets.value_jan,
value_feb: action.budgets.value_feb,
value_mar: action.budgets.value_mar,
value_apr: action.budgets.value_apr,
value_may: action.budgets.value_may,
value_jun: action.budgets.value_jun,
value_jul: action.budgets.value_jul,
value_aug: action.budgets.value_aug,
value_sep: action.budgets.value_sep,
value_oct: action.budgets.value_oct,
value_nov: action.budgets.value_nov,
value_dec: action.budgets.value_dec,
p_version: action.budgets.p_version,
entry_time: action.budgets.entry_time,
campaign: {
...state.campaign, ...action.budgets.campaign
},
segment: {
...state.segment, ...action.budgets.segment
},
touch_point: {
...state.touch_point, ...action.budgets.touch_point
},
year: {
...state.year, ...action.budgets.year
},
user: {
...state.user, ...action.budgets.user
}
}

default:
return state
}



}



I already cannot display data in here - so this.props.fetchBudgets() doesn't seem to fetch any data.



My .jsx App


//./container/PlanContainer.jsx

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


import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';

import 'jquery';
import 'popper.js'
import 'bootstrap';
import 'underscore'
import _ from 'lodash'


import {plan} from "../actions";


const columns = [
{ dataField: 'id', text: 'ID', hidden: true},
{ dataField: 'year', text: 'Year', editable: false},
{ dataField: 'segment', text: 'Segment', editable: false},
{ dataField: 'campaign.name',text: 'Campaign', editable: false},
{ dataField: 'touch_point',text: 'Touchpoint', editable: false},
{ dataField: 'value_jan',text: 'Jan'},
{ dataField: 'value_feb',text: 'Feb'},
{ dataField: 'value_mar',text: 'Mar'},
{ dataField: 'value_apr',text: 'Apr'},
{ dataField: 'value_may',text: 'May'},
{ dataField: 'value_jun',text: 'Jun'},
{ dataField: 'value_jul',text: 'Jul'},
{ dataField: 'value_aug',text: 'Aug'},
{ dataField: 'value_sep',text: 'Sep'},
{ dataField: 'value_oct',text: 'Oct'},
{ dataField: 'value_nov',text: 'Nov'},
{ dataField: 'value_dec',text: 'Dec'},
{ dataField: 'user',text: 'User'},
];

const RemoteCellEdit = (props) => {

const { columns, data, keyField } = props

const cellEdit = {
mode: 'click',
errorMessage: props.errorMessage,
blurToSave: true
};

return (






);
};

class PlanContainer extends React.Component {

componentDidMount() {

this.props.fetchBudgets();
console.log(this.props.fetchBudgets())

}




render() {
return (






);
}
}


const mapStateToProps = state => {
return {
budgets: state.budgets,
}
}

const mapDispatchToProps = dispatch => {
return {
fetchBudgets: () => {
dispatch(plan.fetchBudgets());
},
}
}



export default connect(mapStateToProps, mapDispatchToProps)(PlanContainer);



Finally, my store - according to the console.log nothing is beeing passed:


// .Planning.jsx

import React from "react"
import { hot } from 'react-hot-loader'
import { render } from "react-dom"
import {
createStore,
compose,
applyMiddleware,
combineReducers,
} from "redux"
import { Provider } from "react-redux"
import thunk from "redux-thunk"

import PlanContainer from "./containers/PlanContainer"
import reducerApp from "./reducers";
import Sidebar from "./components/Sidebar"

import axios from 'axios';
import axiosMiddleware from 'redux-axios-middleware';



let store = createStore(reducerApp, applyMiddleware(thunk, axiosMiddleware(axios)));

console.log(store)

class Planning extends React.Component {
render() {
return (

<Sidebar>
<Provider store={store}>
<PlanContainer />
</Provider>
</Sidebar>

)
}
}

render(<Planning />, document.getElementById('Planning'))



Again, I would appreciate as I've been stuck on this issue for quite some time and I really want to understand how to do this properly.



Edit:
Here's a screenshot of my browser: 1st element is the store, second in the .jsx app, 3rd of the action (that looks perfectly fine) and 4th of the action in the reducer.
Store, jsx, action, reducer





Your action is {type: "FETCH_BUDGETS", budgets: response.data} and in the reducer you have return [...state, action.budgets.data] where action.budgets === response.data (I'm assuming the syntax error is a typo). To me this looks like you have an extra .data in the reducer that you probably don't need.
– Yoshi
Jul 2 at 13:05



{type: "FETCH_BUDGETS", budgets: response.data}


return [...state, action.budgets.data]


action.budgets === response.data


.data





Hi Yoshi, thanks - you are totally right. But unfortunately it doesn't solve my issue. I update my post with a pic of my browser's console - the first element is the store, which doesn't really receive anything from the reducer (4th element).
– cesco
Jul 2 at 13:15






What are the actual data-types you're dealing with? In your reducer you're handling the data as an array, shouldn't it then be [...state, ...action.budgets.data] (or similar). Otherwise action.budgets.data would simply be inserted at the next index after ...state. That said, you could try simply returning action.budgets as the new state.
– Yoshi
Jul 2 at 13:22



[...state, ...action.budgets.data]


action.budgets.data


...state


action.budgets





I added a screenshot of whatever the Axios API fetches from the database - it also shows the data types I'm using. The API works perfectly fine - my guess is that I am missing something in the reducer. The reducer is currently [...state, ...action.budgets.data] - but the issue remains the same.
– cesco
Jul 2 at 13:51






That's weird, I don't really see what could be causing the issue. I don't think this would fix it, but you should make your api calls in the componentDidMount lifecycle method
– Zuma
Jul 2 at 13:55




1 Answer
1



PlanContainer is messed up. Here's how:


PlanContainer


componentDidMount() {
this.budgets = this.props.fetchBudgets();
}



this.budgets is pointing to the value returned by this.props.fetchBudgets() which, in this case, is a Promise, and not the actual data.


this.budgets


this.props.fetchBudgets()


state = {
data: this.budgets
};



state now holds the promise, not the data.


state


render() {
return (




So data here is not the actual data but the promise.


data



The confusion is happening because you are mixing redux state with react state. Use one or the other, not both (there are expcetions to this but not in this particular scenario).



There are some more issues with PlanContainer which are not clear as to whether they are real issues, or just a result of code ommission in OP.


PlanContainer



See annotations below:


class PlanContainer extends React.Component {

componentDidMount() {
this.props.fetchBudgets();
}

constructor(props) {
... // removed for brevity, use the same code as you have right now
}



render() {
return (





{this.props.budgets} /* not sure what this is for - I assumed RemoteCellEdit is the one rendering the data */



);
}
}



Fixing these should set you on the correct course. Good luck!





see edit: adjusted the code to your suggestions.
– cesco
Jul 2 at 15:55





Now reducer is returning an object instead of an array. Are you sure you want to do that? You can't render the table that way. Also, a sidenote: if your goal is to display the budgets returned by the API, then you shouldn't be merging/concatenating it existing state. In your reducer, you can simply return action.budgets.
– Mrchief
Jul 2 at 17:39


action.budgets





Also, it'd help if you can create a codesandbox (you can fork this one: codesandbox.io/s/jpn9m4rwr9) and use something like jsonapi or mockaxios to fake axios calls.
– Mrchief
Jul 2 at 17:41





thank you so much! I will try to transfer the insights to my code - try to update soon.
– cesco
Jul 2 at 17:55





changing the reducer index, adding the separate store (both similar to the codesandbox that you posted) and implementing Object spread in the plan reducer did the trick. Again, thank you a lot!
– cesco
Jul 3 at 17:40



changing the reducer index, adding the separate store


implementing Object spread






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

YtWTQXuG06xpLfQotR1NIfl,pVE8uDX,zKj8XqjOXeGWVULDPdM7dYAVKgShKm Eb,BLV zxgNgS rCY,ULjVn9W QFH
YWOwv1MODvt BcFNeJ,2pF2O0lt7efEq6QsL,tjz8ROtquD

Popular posts from this blog

PHP contact form sending but not receiving emails

Do graphics cards have individual ID by which single devices can be distinguished?

Create weekly swift ios local notifications