0

I'm new to Redux, how would I reference the state of a nested array in my reducer?

Here is my state:

const initState = {
  tabs: [
    { arr: [] },
    { arr: [] }
  ]
};

I tried doing something like:

tabs[0].arr: //do stuff

inside my reducer, but I get a parsing error. What is the correct way to reference the array in the first tab?

Edit: I am adding more code for clarity

function reducer(state = initState, action) {
  if (action.type == 'ADD_ARR') {
      return {
          state.tabs[0].arr: [...state.tabs[0].arr, action.arr]
      }
 }
}
//I get this message: 'Parsing error: Unexpected token, expected ","' at the period between 'state' and 'tabs[0]'

The rest of my code

const arrAction = { type: "ADD_ARR", arr: "hello" };
store.dispatch(arrAction);
Evan Hsueh
  • 139
  • 1
  • 2
  • 9

2 Answers2

1

Try to avoid state mutation, by not updating the array directly. You also need to pass an index of the array you want to modify.

const arrAction = { type: "ADD_ARR", arr: "hello", index: 0 };
store.dispatch(arrAction);

function reducer(state = initState, action) {
  if (action.type == 'ADD_ARR') {

    const index = action.index;
    const arr = [...state.tabs[index].arr, action.arr];

    return {
      ...state,
      tabs: [...state.tabs.slice(0, index),
        Object.assign({}, state.tabs[index], { arr: [...arr] }),
        ...state.tabs.slice(index + 1)
      ]
    };
  }
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.1/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.0.3/react-redux.min.js"></script>
<script src="http://wzrd.in/standalone/uuid%2Fv1@latest"></script>

<div id="root"></div>

    <script type="text/babel">
      const { Provider, connect } = ReactRedux;
      const { applyMiddleware, createStore, combineReducers } = Redux;


      function addArr(payload) {
        return { type: 'ADD_ARR', payload}
      }

      const initialState = {
        tabs: [{ arr: [] }, { arr: [] }]
      };

      function rootReducer(state = initialState, action) {

        if (action.type == 'ADD_ARR') {
          const index = action.payload.index;
          const arr = [...state.tabs[index].arr, action.payload.title];

          return {
            ...state,
            tabs: [
              ...state.tabs.slice(0, index),
              Object.assign({}, state.tabs[index], { arr: [...arr] }),
              ...state.tabs.slice(index + 1)
            ]
          };
        }
        return state;
      }

      const store = createStore(rootReducer);

      const mapStateToProps = state => {
        return { tabs: state.tabs };
      };

      function mapDispatchToProps(dispatch) {
        return {
          addExpense: expense => dispatch(addExpense(expense)),
          addArr: e => dispatch(addArr(e))
        };
      }

      const ConnectedList = ({ tabs, addExpense }) => {

        const tab1 = tabs[0].arr;
        const tab2 = tabs[1].arr;

        return (
          <div>
            {tab1.length > 0 && <p>tabs [0] arr values</p>}
            <ul className="list-group list-group-flush">
              {tab1.map((el, index) => (
                <li className="list-group-item" key={index}>
                  {el}
                </li>
              ))}
            </ul>

            {tab2.length > 0 && <p>tabs [1] arr values</p>}
            <ul className="list-group list-group-flush">
              {tab2.map((el, index) => (
                <li className="list-group-item" key={index}>
                  {el}
                </li>
              ))}
            </ul>
          </div>
        );
      };

      const List = connect(mapStateToProps)(ConnectedList);

      class StuffForm extends React.Component {
        state = {
          title: '',
        };

        handleSubmit = (index) => {

          if (!this.state.title) {
            return;
          }

          const { title} = this.state;

          this.props.addArr({
            title,
            index
          });

          this.setState({
            title: '',
          });
        };

        handleInput = e => {
          this.setState({
            [e.target.name]: e.target.value
          });
        };

        render() {
          return (
            <div>
              <input
                name="title"
                placeholder="title"
                onChange={this.handleInput}
                value={this.state.title}
              />
            <button onClick={() => this.handleSubmit(0)}>Add to arr[0]</button>
            <button onClick={() => this.handleSubmit(1)}>Add to arr[1]</button>
              </div>
          );
        }
      }

      const Form = connect(
        null,
        mapDispatchToProps
      )(StuffForm);

      class App extends React.Component {
        render() {
          return (
            <div>
              <List />
              <Form />
            </div>
          );
        }
      }
      ReactDOM.render(
        <Provider store={store}>
          <App />
        </Provider>,
        document.getElementById('root')
      );
    </script>
Junius L
  • 15,881
  • 6
  • 52
  • 96
  • I'm getting an error: "Uncaught TypeError: Invalid attempt to spread non-iterable instance" on the line "const arr = [...state.tabs[index].arr, action.arr];" – Evan Hsueh May 26 '19 at 21:17
  • means `state.tabs[index].arr` is not an array. – Junius L May 26 '19 at 21:24
  • @EvanHsueh I think I was missing `...state` in my return should work now, see my updated answer and a demo. – Junius L Jun 01 '19 at 19:49
0

Your syntax is wrong for updating array.

function reducer(state = initState, action) {
  if (action.type == 'ADD_ARR') {
     return {
        tabs: [ {
           arr: action.arr
        }, ...state.tabs]
  }
}
Amit Chauhan
  • 6,151
  • 2
  • 24
  • 37