Abrar
Abrar

Reputation: 7232

React-Redux's mapStateToProps working in unexpected way

I have a component which shows a list of names fetched from the Redux store. The component looks like this:

class Details extends React.Component {

  constructor (props) {
    super(props);
  }

  render () {
    const listName = [...this.props.listName];
    return (
      <div className="container detailsBox">
        <p>Details Grid:</p>
        {listName ? (
          listName.map((el, index) => {
            return (
              <li key={index}>
                {el}
              </li>
            )
          })
        ): null}
      </div>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    listName: state.listName
  }
}

export default connect(mapStateToProps)(Details);

Notice here I only map the listName from the store. But the code does not display the <li> with the elements of listName even when logging in console shows listName is populated with data

But data shows as expected when the name is also fetched from the store, with this modification in mapStateToProps:

const mapStateToProps = (state) => {
  return {
    listName: state.listName,
    name: state.name
  }
}

I am befuddled as well as curious to know why the code behaves in this unexpected way? What am I missing here?

The reducer code looks like this:

import { UPDATE_NAME, ADD_NAME } from '../actions/addName.action';

const initialState = {
  name: '',
  listName: new Set()
}

function rootReducer (state = initialState, action) {
  switch (action.type) {
    case ADD_NAME:
      return {
        name: '',
        listName: state.listName.add(action.payload)
      }
    case UPDATE_NAME:
      return {
        ...state,
        name: action.payload
      }
    default:
      return state;
  }
}

export default rootReducer;

and the actions like this:

export const ADD_NAME = 'ADD_NAME';
export const UPDATE_NAME = 'UPDATE_NAME';

export function addName (data) {
  return {
    type: ADD_NAME,
    payload: data
  }
}

export function updateName (name) {
  return {
    type: UPDATE_NAME,
    payload: name
  }
}

Upvotes: 0

Views: 66

Answers (2)

ValeryStatinov
ValeryStatinov

Reputation: 169

This is happens because you mutate the state. When you mutate listNames it still points to the same object in memory, and react thinks that nothing is changed, while the contents of listNames is changed. You should return a new object.

case ADD_NAME:
  return {
    name: '',
    listName: new Set([...state.listName, action.payload]),
  }

But it is not recommended to use Sets in reducers. Instead you can store your listNames as an Array and use lodash union function to have only unique values.

import { union } from 'lodash';

const initialState = {
  name: '',
  listName: []
}

// some code

case ADD_NAME:
  return {
    name: '',
    listName: union(state.listNames, action.payload)
  }

Upvotes: 2

handsben
handsben

Reputation: 52

You can instead use an object to hold the listName and then recreate the structure of your state into a new object and then return.

const initialState = {
  name: '',
  listName: {}
}

case ADD_NAME:
  return {
    name: '',
    listName: {...state.listName, action.payload}
  }

Upvotes: 0

Related Questions