Cooper Campbell
Cooper Campbell

Reputation: 163

React turns a state key into a promise

I have a relatively simple React app using redux architecture, and for some reason one of the state properties is being changed to a promise.

I am eventually going to try to pull data from a database to use as data, but I am starting with hard-coded data. I cannot figure out why in the world the Charts key, by the time it reaches the layout/charts/index component why it has changed to be a Promise? I haven't included any api/async functionality in my app from what I can tell. I have included all the files that I think are relevant to the issue. I have tried console.logging the state in various places and I cannot seem to identify when or why the state decides to change to a promise. Any help would be much appreciated.

GitHub Repo of project here.

app.js

import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { browserHistory } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import { AppContainer } from 'react-hot-loader';
import configureStore from './store/configureStore';
import Root from './containers/Root';

const initialState = {
    Charts: {
        PieChart: {
            data: {
                failed: false,
                isfetching: false,
                contains: null
            },
            msg: 'Preparing to fetch',
            width: '100%',
            height: '30vh',
            options: {
                title: 'Lateness of things',
                backgroundColor: '#fff',
                titlePosition: 'none',
                pieHole: 0.7,
                pieSliceTextStyle: {
                    color: 'black',
                },
            }
        },
        BarChart: {
            chartType: 'BarChart',
            width: '100%',
            height: '30vh',
            data: [
                ['Type', 'On time', 'Late', { role: 'annotation' }],
                ['Child', 4, 18, ''],
                ['Fire/EV/Body', 18, 21, ''],
                ['Truck', 49, 92, ''],
                ['Off-Highway/UTV', 18, 62, ''],
                ['Bus/Coach/WTORS', 5, 8, ''],
                ['Other', 11, 23, '']
            ],
            options: {
                isStacked: true,
                height: 300,
                legend: {position: 'top'},
                hAxis: {minValue: 0}
            }
        }
    }
};
const store = configureStore(initialState);
const history = syncHistoryWithStore(browserHistory, store);

render(
    <AppContainer>
        <Root store={store} history={history}/>
    </AppContainer>,
    document.getElementById('root'),
);

if(process.env.NODE_ENV !== 'production' && module.hot) {
    module.hot.accept('./containers/Root', () => {
        const NewRoot = require('./containers/Root').default;
        render(
            <AppContainer>
                <NewRoot store={store} history={history}/>
            </AppContainer>,
            document.getElementById('root'),
        );
    });
}

reducer.js

import { routerReducer as routing } from 'react-router-redux';
import { combineReducers } from 'redux';
import * as types from '../actions/types';

const Charts = async (state = {}, action) => {
    switch(action.type) {
        case types.PIE_DATA_LOADING:
            return {...state, PieChart: {isfetching: true, contains: null, failed: false}};
        default:
            return state;
    }
};


const rootReducer = combineReducers({
    Charts,
    routing,
});

export default rootReducer;

container/Charts.js

import MainChart from './../components/layout/charts';
import {connect} from 'react-redux';
import { startPieDataLoad } from './../actions';

const mapStateToProps = (state) => {
    return {
        Charts: state.Charts,
    };
};

const mapDispatchToProps = (dispatch) => {
    return {
        loadingPieChartData: () => {
            return dispatch(startPieDataLoad());
        }
    };
};


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

components/layout/charts/index.js

import React from 'react';
import classNames from 'classnames';
import TotalChanges from './TotalChanges';
import ChangesByFunctionalGroups from './ChangesByFunctionalGroups';
import PropTypes from 'prop-types';

const MainChart = ({Charts}) => {
    const BarChartData = Charts.BarChart;
    const PieChartData = Charts.PieChart;
    const PieChart = (!PieChartData.data.isfetching === false) ? (<TotalChanges chartData={PieChart} />) : (<div>{PieChartData.msg}</div>);
    return (
        <div className={classNames('mainWindow')}>
            <div className={classNames('row')}>
                <div className={classNames('col-sm-4')}>
                    {PieChart}
                </div>
                <div className={classNames('col-sm-4')}>
                    <ChangesByFunctionalGroups chartData={BarChartData} />
                </div>
                <div className={classNames('col-sm-4')}>
                </div>
            </div>
        </div>
    );
};

MainChart.propTypes = {
    Charts: PropTypes.object,
    loadingPieChartData: PropTypes.func
};

export default MainChart;

configureStore.js

import { createStore } from 'redux';
import rootReducer from '../reducers';

export default function configureStore(initialState) {
    return createStore(
        rootReducer,
        initialState
    );
};

action.js

import * as types from './types';
import fetch from 'isomorphic-fetch';
export function example(filter) {
    return {
        type: types.FILTER,
        filter,
    };
}

export function startPieDataLoad() {
    return {
        type: types.PIE_DATA_LOADING
    };
};

export function finishPieDataLoad(data) {
    return {
        type: (data.err === true) ? types.PIE_DATA_LOADED_FAIL : types.PIE_DATA_LOADED_SUCCESS,
        data: data.msg
    };
};


export function fetchPieChartData() {
    return (dispatch) => {
        dispatch(startPieDataLoad);
        return fetch('http://localhost:3001/cm/piechart').then(response => response.json()).then(json => dispatch(finishPieDataLoad(json)));
    };
};

Upvotes: 1

Views: 132

Answers (2)

Cooper Campbell
Cooper Campbell

Reputation: 163

The issue was I was declaring the action as an async function which caused it to return a promise. I hadn't realized that was there which is why it took me three hours to find it.

The issue is in reducer.js

import { routerReducer as routing } from 'react-router-redux';
import { combineReducers } from 'redux';
import * as types from '../actions/types';

const Charts = (state = {}, action) => {
    switch(action.type) {
        case types.PIE_DATA_LOADING:
            return {...state, PieChart: {isfetching: true, contains: null, failed: false}};
        default:
            return state;
    }
};


const rootReducer = combineReducers({
    Charts,
    routing,
});

export default rootReducer;

Note the lack of async after the Charts =

Upvotes: 1

AJ_
AJ_

Reputation: 3987

First things first. In Redux, you never mutate state. In your reducer use object.assign

    case types.PIE_DATA_LOADING:
        return {...state,
            Object.assign({}, {PieChart: {isfetching: true, contains: null, failed: false}})

Upvotes: 1

Related Questions