0

Cannot read properties of undefined (reading 'name') TypeError: Cannot read properties of undefined (reading 'name')

index.js

import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {legacy_createStore as createStore} from 'redux'
import { Provider } from 'react-redux';

const store = createStore((state = [], action) => {
  if (action.type === 'ADD') return [...state, action.name];
  return state;
});

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

reportWebVitals();

App.js

import React, { createRef } from 'react';
import { connect } from 'react-redux';

const Item = ({ name, price }) => (<li>{name},${price}</li>)
       
const App = (props) => {
  let nameRef = createRef();
  let priceRef = createRef();

  const add = () => {
    props.add(
      props.items.length +1,
      nameRef.current.value,
      priceRef.current.value,
    )
  }

  return (
    <div>
      <ul>
        {props.items.map(i => (
          <Item key={i.id} name={i.name} price={i.price} />
        ))}
      </ul>

      <input type="text" ref={nameRef} />
      <input type="text" ref={priceRef} />
      <button onClick={add}>Add</button>
    </div>
  )
}

const stateToProps = state => {
  return {
    items: state
  }
}

const dispatchToProps = dispatch => {
  return {
    add: (id, name, price) => {
      dispatch ({
        type: 'ADD',
        item: { id, name, price }
      })
    }
  }
}

const ReduxApp = connect(stateToProps, dispatchToProps)(App);
export default ReduxApp

Errorenter image description here Cannot read properties of undefined (reading 'name') TypeError: Cannot read properties of undefined (reading 'name')

enter image description here

1 Answer 1

1

Issue

I don't know why the code didn't throw an error on i.id, but the issue is that you are pushing undefined into the state array each time the ADD action is dispatched.

add: (id, name, price) => {
  dispatch ({
    type: 'ADD',
    item: { id, name, price }
  })
}

The action object has two properties: type and item, but when the dispatched action is processed in the reducer function, action.name, which is undefined is appended into the array.

const store = createStore(
  (state = [], action) => {
    if (action.type === 'ADD') return [...state, action.name];
    return state;
  }
);
state: [undefined, undefined, undefined, ....etc...]

The state is selected and injected into the App component as props.items and when mapped, each array element value is undefined and the code attempts to access properties and the error is thrown.

<ul>
  {props.items.map(i => (
    <Item
      key={i.id}
      name={i.name} // <-- oops, i is undefined!
      price={i.price}
    />
  ))}
</ul>

Solution

Access the ADD action payload correctly. You are using very dated redux patterns, so I'll rewrite portions of the code that align with more conventional redux, which will make upgrading or working with modern redux easier for you.

index.js

import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {legacy_createStore as createStore} from 'redux'
import { Provider } from 'react-redux';

const rootReducer = (state = [], action) => {
  switch(action.type) {
    case 'ADD':
      return [...state, action.payload];

    default:
      return state;
  }
};

const store = createStore(rootReducer);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

App.js

import React, { createRef } from 'react';
import { connect } from 'react-redux';

const Item = ({ name, price }) => <li>{name}, ${price}</li>;

const addItem = (payload) => ({
  type: 'ADD',
  payload
});
       
const App = ({ addItem, items }) => {
  const nameRef = createRef();
  const priceRef = createRef();

  const add = () => {
    addItem({
      id: items.length + 1,
      name: nameRef.current.value,
      price: priceRef.current.value,
    });
  };

  return (
    <div>
      <ul>
        {items.map(item => (
          <Item key={item.id} name={item.name} price={item.price} />
        ))}
      </ul>
      <input type="text" ref={nameRef} />
      <input type="text" ref={priceRef} />
      <button type="button" onClick={add}>Add</button>
    </div>
  )
}

const mapStateToProps = state => ({
  items: state
});

const mapDispatchToProps =  {
  addItem
}

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

A more modern version would use the useDispatch and useSelector hooks in lieu of the outdated connect Higher Order Component.

App.js

import React, { createRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';

const Item = ({ name, price }) => <li>{name}, ${price}</li>;

const addItem = (payload) => ({
  type: 'ADD',
  payload
});
       
const App = () => {
  const dispatch = useDispatch();
  const items = useSelector(state => state);

  const nameRef = createRef();
  const priceRef = createRef();

  const add = () => {
    dispatch(addItem({
      id: items.length + 1,
      name: nameRef.current.value,
      price: priceRef.current.value,
    }));
  };

  return (
    <div>
      <ul>
        {items.map(item => (
          <Item key={item.id} name={item.name} price={item.price} />
        ))}
      </ul>
      <input type="text" ref={nameRef} />
      <input type="text" ref={priceRef} />
      <button type="button" onClick={add}>Add</button>
    </div>
  )
}

export default App;
Sign up to request clarification or add additional context in comments.

Comments

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.