Jay P.
Jay P.

Reputation: 2590

How can I keep previous state and add a new state to the previous state in Redux reducers?

The below is one of my reducers. In the function applySetRandomImages, I want to access previous state of randomImages to add a new state of randomImages with the previous state.

How can I do that? does the reducer function in Redux provides some call back function for that? or should I implement that on my own?

// ACTIONS
const SET_RANDOM_IMAGES = "SET_RANDOM_IMAGES";

// ACTION CREATORS
function setRandomImages(randomImages) {
  return {
    type: SET_RANDOM_IMAGES,
    randomImages
  };
}

// API ACTIONS
function getRandomImages(page) {
  return (dispatch, getState) => {
    fetch(`/boutiques/random-images/?page=${page}`)
      .then(response => response.json())
      .then(json => dispatch(setRandomImages(json.results)))
      .catch(err => console.log(err));
  };
}

// INITIAL STATE
const initialState = {};

// REDUCER
function reducer(state = initialState, action) {
  switch (action.type) {
    case SET_RANDOM_IMAGES:
      return applySetRandomImages(state, action);
    default:
      return state;
  }
}

// REDUCER FUNCTIONS
function applySetRandomImages(state, action) {
  const { randomImages } = action;
  return {
    ...state,
    randomImages    <--- I need to merge the randomImages with a new state of randomImages
  };
}

// EXPORTS
const actionCreators = {
  getRandomImages,
};
export { actionCreators };

// DEFAULT REDUCER EXPORTS
export default reducer;

Upvotes: 1

Views: 4399

Answers (3)

devserkan
devserkan

Reputation: 17608

You can merge randomImages by spreading the old state and the new one into a new array:

function applySetRandomImages(state, action) {
  const { randomImages } = action;
  return {
    ...state,
    randomImages: [...state.randomImages, ...randomImages],
  };
}

Upvotes: 3

Matt Carlotta
Matt Carlotta

Reputation: 19762

Separate actions, reducers and types into their own folders.

types/index.js

export const SET_RANDOM_IMAGES = "SET_RANDOM_IMAGES";

actions/imageActions.js

import * as types from '../types';

export const getRandomImages = page => dispatch => (
  fetch(`/boutiques/random-images/?page=${page}`)
    .then(response => response.json())
    .then(json => dispatch({ type: types.SET_RANDOM_IMAGES, payload: json.results })))
    .catch(err => console.log(err))
)

From within a component, you will connect to redux state (state.images or state.images.collection) and dispatch the action (getRandomImages):

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getRandomImages } from '../actions/imageActions';

class Example extends Component {

   componentDidMount = () => this.props.getRandomImages(); // dispatches action creator

   render = () => (
       <div>
         { /* 
             The props below are redux state passed into 
             the component via the connect function
         */ }
         {this.props.images.map(image => (
           <img src={image.src} alt="image.name" />
         ))}
         {this.props.collection.map(image => (
           <img src={image.src} alt="image.name" />
         ))}
       </div>
   )
}

export default connect(state => ({ 
  images: state.images, // pulled from redux state (state.images => this.props.images)
  collection: state.images.collection // pulled from redux state (state.images.collection => this.props.images.collection)
}), { getRandomImages})(Example)

It will then trigger the AJAX request, then return a type and payload to your reducer:

reducers/index.js

import * as types from '../types'

// overwrite images for each successful fetch request
const imageReducer(state={}, {type, payload}) {
  switch (type) {
    // spread out any previous state, then spread out the payload (json.results)
    case types.SET_RANDOM_IMAGES: return { ...state, ...payload }
    default: return state;
  }
}

// or append images on each successful fetch request...
const imageReducer(state={}, {type, payload}) {
  switch (type) {
    case types.SET_RANDOM_IMAGES: 
      return { 
        ...state,  // spread out any previous state
        collection: [ 
          ...state.collection, //  then spread out any previous "collection" state, 
          ...payload // then spread/append the payload (json.results) 
        ]
      }
    default: return state;
  }
}

export default combineReducers({
  images: imageReducer
});

The reducer will then spread out any previous imageReducer state, then append the res.results to it via payload. Now it exists in redux as state.images or state.images.collection. This is then pulled from redux state and into the component above as this.props.images or this.props.images.collection.

Upvotes: 1

brietsparks
brietsparks

Reputation: 5016

I want to access previous state of randomImages to add a new state of randomImages with the previous state

return {
    ...state,
    ...randomImages
};

If previous state was:

{
    a: 1,
    b: 2,
    c: 3
}

And randomImages is:

{
    d: 4,
    e: 5
}

Then the returned new state will be

{
    a: 1,
    b: 2,
    c: 3,
    d: 4,
    e: 5
}

Upvotes: 0

Related Questions