5

I'm using Redux in my React App, currently only have 1 reducer at the moment for a Portfolio of cryptocurrencies. Just an array of coins.

When I dispatch my ADD_COIN action the first time I get back an Array of 1 object which is expected. However when I call dispatch again on another coin it returns the Array again, but still only with 1 item, the latest coin that was selected.

How would this need to be re-written to return back the updated Array with all added coins?

1st coin added enter image description here

2nd coin added, expected to see an Array with 2 objects: enter image description here

index.js the store code

const store = createStore(reducer, compose(
    applyMiddleware(thunk),
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
));

The reducer file

import { combineReducers } from 'redux'
import portfolio from './portfolio'

export default combineReducers({
    portfolio
});

The actions file

import * as R from 'ramda'
import * as api from '../../services/api'
import { addToPortfolio, getPortfolio } from '../../services/coinFactory'

export const ADD_COIN = 'ADD_COIN'

export function addCoin(coin) {
    return dispatch =>
        api.getCoin(coin)
            .then((res) => addToPortfolio(R.head(res.data)))
            .then((portfolio) => dispatch(add(portfolio)));
}

//action creator
export function add(portfolio) {
    return {
        type: ADD_COIN,
        portfolio
    }
}

The Portfolio reducer

import { ADD_COIN } from './actions'

const initialState = [];

export default (state = initialState, action) => {
    switch(action.type) {
        case ADD_COIN:
            return action.portfolio;
        default:
            return state;
    }
}

Finally the coinFactory.js which has the Array

import * as R from 'ramda'
import local_coins from '../coins.json'

export const storage = {
    coins: local_coins,
    portfolio: []
};

export const getPortfolio = () => storage.portfolio;

export const addToPortfolio = (coin) => {
    storage.portfolio.push(coin);
    return getPortfolio();
};

2 Answers 2

15

Instead of mutating your initialState with .push, "copy" your last state into meaningful new state

import { ADD_COIN } from './actions'

const initialState = [];

export default (state = initialState, action) => {
    switch(action.type) {
        case ADD_COIN:
            return [...state, action.portfolio]; // same as state.concat(action.portfolio)
        default:
            return state;
    }
}
Sign up to request clarification or add additional context in comments.

10 Comments

Hmm strange, I'm running into the same original problem now... an Array with 1 item, debugging
are you still using this function addToPortfolio ?
Oh I think I see... storage.portfolio returns an array
so you're trying to "add a coin" but you're sending the entire portfoilio, so your new state is an array with a portfolio array inside of it
cool if it's open I'll take a look. join our slack for further help. ping @alex-wilmer slack.torontojs.com
|
3

I figured it out, I was incorrectly trying to use 2 different design patterns. The factory pattern with Redux... not actually bad, but it made me make this mistake.

import { ADD_COIN } from './actions'

const initialState = [];

export default (state = initialState, action) => {
    switch(action.type) {
        case ADD_COIN:
            return initialState.push(action.portfolio);
        default:
            return state;
    }
}

initialState is suppose to be my Portfolio array. So in the case of action ADD_COIN, I need to push it into that array, not the one from my factory (though I guess I could use the factory Array and set it to initialState)

Then in my actions I sent out just the coin, not a coin already inside of another Array.

import * as R from 'ramda'
import * as api from '../../services/api'

export const ADD_COIN = 'ADD_COIN'

export function addCoin(coin) {
    return dispatch =>
        api.getCoin(coin)
            .then((res) => R.head(res.data))
            .then((remote_coin) => dispatch(add(remote_coin)));
}

// action creator
export function add(portfolio) {
    return {
        type: ADD_COIN,
        portfolio
    }
}

Now the state of the Portfolio Array updates correctly

enter image description here

enter image description here

If someone has a better more description answer to this I'll mark their answer instead of this.

4 Comments

@azium yes I am, do you recommend a better alternative?
yes .concat or [...state, coin]. redux specifically says to not mutate state anywhere
also you're adding an item to intitialState, not state, so you can only ever have one item in your array at most, which must be a mistake
Ah that is better, do you want to post your answer with that spread operator? I don't like checking my own answers, but others.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.