0

I have got the data from reducer and display it in console console display

but when i want to access specific object for example (table.nomor) and it become error enter image description here

this is for my reducer:

export const orderListDetailReducer = (state = { orders: {} }, action) => {
switch (action.type) {
    case ORDER_TABLE:
        return {
            orders: action.payload
        }
    default:
        return state;
}
}

this is for my screen:

import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { listOrderDetail } from '../actions/orderAction'


const PlaceOrderScreen = (props) => {
const orderId = props.match.params.id
const orderListDetail = useSelector(state => state.orderListDetail)
const { orders } = orderListDetail

const dispatch = useDispatch()

useEffect(() => {
    dispatch(listOrderDetail(orderId))
}, [dispatch, orderId])

console.log(orders.table.nomor);
5
  • 1
    Try to log orders object in your component. It should contain just the initial state, since useEffect() doesn't change the state in the current render Commented Feb 23, 2021 at 15:32
  • it same, i only can access the object (orders.table), but cannot access the subobject (orders.table.nomor) Commented Feb 23, 2021 at 15:51
  • It's supposed to be the same. I'm trying to say that you don't have problem with access but rather with initialization, since orders is in it's initial state when you try to access its properties. Does it contain orders.table field in initial state? Commented Feb 23, 2021 at 15:59
  • ya it containts orders.table Commented Feb 23, 2021 at 16:19
  • 1
    useEffect is executed after the initial render. Commented Feb 23, 2021 at 18:02

2 Answers 2

2

Problem: undefined in initial state

The first render of your component happens before the dispatched action has completed its update of the store. That means that the value of state on your first render is the initial value (unless you have already updated the state from some other component).

Your initial state is state = { orders: {} } so the initial value of orders is an empty object {}. You can access orders.table on the empty object, but the value of orders.table is undefined. You cannot access orders.table.nomor because that would be trying to get the property nomor from orders.table which is undefined -- so you get the error "Cannot read property 'nomor' of undefined".

You need to make sure that your component can render properly at any point in the lifecycle, whether or not the ORDER_TABLE action has been completed.

Solution 1: guarded access

You can make sure that orders.table is not undefined before accessing the .nomor property.

const nomor = orders.table ? orders.table.nomor : someDefaultValue;

Or more concisely with optional chaining.

const nomor = orders.table?.nomor; // will be undefined if not able to access

Solution 2: initial value

You can set a deeper initial value object for your reducer which allows you to access orders.table.nomor. This may or may not make sense depending on your app's data structures.

If orders.table is an empty object {} instead of undefined, then it will be ok to access orders.table.nomor. The value of orders.table.nomor in this case is undefined.

const initialState = {
  orders: {
     table: {}
  }
}

You can also go one level deeper and set an initial value for nomor.

const initialState = {
  orders: {
     table: {
       nomor: someInitialValue
     }
  }
}

Either way, you would use this as the initial value for your reducer.

export const orderListDetailReducer = (state = initialState, action) => {
Sign up to request clarification or add additional context in comments.

Comments

1

this is common issue with async behavior on react-redux store, you need to understand useSelector works async and to check the variable with full values you need to use a useEffec, check the following example:

***try the code and tell me how goes :)

import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { listOrderDetail } from '../actions/orderAction'


const PlaceOrderScreen = (props) => {
const orderId = props.match.params.id
const orders = useSelector(state => state.orderListDetail.orders)//-->changed this line -->added one level
//const { orders } = orderListDetail //-->commented this line

const dispatch = useDispatch()

useEffect(() => {
    dispatch(listOrderDetail(orderId))
}, [dispatch, orderId])


//-->create new useEffect and add the dependency from useSelector constant
useEffect(() => {
    console.log('orders',orders);
    console.log('nomor',orders.table.nomor);
}, [orders]);

1 Comment

that can be because of the level on the object try doing a desctructuring like : const { table } = orders ; then console log --> console.log(table.nomor)

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.