0

in LisPage.js when component loads its give props.detail undefined after showCardDetail() is called. In logger it is clearly called, but didnt understand why it shows undefined. I tried setTimeout() but still same result. Even that showCardDetail() also console.log(card) properly, but still payload return that undefined.

store.js

import { createStore, applyMiddleware } from 'redux'
import counter from '../redux/action';
import thunk from 'redux-thunk';
import {reducer} from './reducer';
import logger from 'redux-logger';
import {composeWithDevTools} from 'redux-devtools-extension/developmentOnly';

const middleware = [thunk]

// const logger = createLogger({
//     /* https://github.com/evgenyrodionov/redux-logger */
//     collapsed: true,
//     diff: true
// });

export const store = createStore(
    reducer,
    composeWithDevTools(
        /* logger must be the last middleware in chain to log actions */
        applyMiddleware(thunk, logger)  
    )
    )

reducer.js

import data from '../data.json';
import {showCard} from './dispatchAction';
const initialState = {
    value:data,
    filterValue:[],
    detail:[]
}
export const reducer = (state = initialState, action) => {
    switch (action.type){
        case 'SHOW_DATA':
            return {
                value: action.payload
            }
        case 'SHOW_CARD':
            return [
                state.detail = action.payload
            ]
    }
    return state
}

dispatchAction.js

import data from '../data.json';


export const showDATA = () => dispatch => {
    dispatch({type:'SHOW_DATA', payload:data})
}

export const showCardDetail = (id) => async dispatch => {
    // let card = {}
    // data.map((d, i) => {
    //     if (d.id === id){
    //         console.log('data from red', d);
    //         card = d;
    //     }
    // });
    const card = data.filter(d => d.id === id)
    console.log(card);
    await dispatch({type:'SHOW_CARD', payload:card})
};

ListPage.js

/* eslint-disable prettier/prettier */
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable prettier/prettier */
import React, {useState, useEffect} from 'react';
import { useSelector, useDispatch } from 'react-redux';
import {connect} from 'react-redux';
import { showCardDetail, showDATA } from '../../../redux/dispatchAction';
import show from '../../../redux/action';
import {
  View,
  Text,
  SafeAreaView,
  ScrollView,
  TouchableOpacity,
  Modal,
  Alert,
  Pressable,
} from 'react-native';
import WareCard from '../../components/WareCard/WareCard';
import styles from './styles';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import DropDownPicker from 'react-native-dropdown-picker';
import {
  widthPercentageToDP as wp,
  heightPercentageToDP as hp,
} from 'react-native-responsive-screen';

const arr = [1, 1, 1, 1, 1, 1, 1, 1, 1];

function ListPage(props) {
  const {navigation, value} = props;

  const [modalVisible, setModalVisible] = useState(false);
  const [showContent, setShowContent] = useState();

  useEffect(() => {
    // console.log(showData);
    setShowContent(value && value !== undefined ? value : '');
    // console.log(value)
    props.showCardDetail(2)
    props.showDATA();
    // setTimeout(() => {
    //   console.log("%% ",props.detail, "$$$$$$$$");
    // }, 2000)
    console.log(": ",props.detail, "$$$$$$$$");
  }, []);

  function spaceFilter(){
    let sd = []
    value.map((da, index) => {
      if (da.is_live === true){
        sd.push(da);
      }
      setShowContent(sd)
      setModalVisible(!modalVisible)
    })
  }

  function registerFilter(){
    let sd = []
    value.map((da, index) => {
      if (da.is_registered === true){
        sd.push(da);
      }
      setShowContent(sd)
      setModalVisible(!modalVisible)
    })
  }

  return (
    <SafeAreaView style={styles.page}>
      <View style={styles.header}>
        <Text style={styles.title}>Warehouses</Text>
        <TouchableOpacity onPress={() => setModalVisible(!modalVisible)}>
          <MaterialIcon name="filter-alt" style={styles.filter}></MaterialIcon>
        </TouchableOpacity>
      </View>
      <Modal
        animationType="fade"
        transparent={true}
        visible={modalVisible}
        onRequestClose={() => {
          setModalVisible(!modalVisible);
        }}>
        <View style={styles.centeredView}>
          <View style={styles.modalView}>
            <TouchableOpacity onPress={() => spaceFilter()}>
              <Text style={styles.modalText}>Space available</Text>
            </TouchableOpacity>
            <TouchableOpacity onPress={() => registerFilter()}>
              <Text style={styles.modalText}>Registered</Text>
            </TouchableOpacity>
          </View>
        </View>
      </Modal>
      <ScrollView
        contentContainerStyle={styles.scrollPage}
        showsVerticalScrollIndicator={false}>
        {showContent && showContent !== undefined ?
        showContent.map((data, index) => {
          return (
            <WareCard
              city={data.city}
              cluster={data.cluster}
              name={data.name}
              space_available={data.space_available}
              type={data.type}
              is_live={data.is_live}
              navigation={navigation}
              is_registered={data.is_registered}
              code={data.code}
              key={index}
            />
          );
        })
        :
        null
      }
      </ScrollView>
    </SafeAreaView>
  );
}

function mapStateToProps(state){
  return {
    value: state.value,
    detail: state.detail,
    filterValue:state.filterValue,
  }
}



export default connect(mapStateToProps, {
  showDATA,
  showCardDetail
})(ListPage);

App.js

/* eslint-disable prettier/prettier */
import React from 'react';
import ListPage from './src/screens/ListPage/ListPage';
import DetailPage from './src/screens/DetailPage/DetailPage';
import EditPage from './src/screens/EditPage/EditPage';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import { Provider } from 'react-redux';
import {store} from './redux/store';


function App() {
  const Stack = createNativeStackNavigator();

  return (
    <>
      {/* <ListPage /> */}
      <Provider store={store}>
      <NavigationContainer>
        <Stack.Navigator screenOptions={{headerShown: false}}>
          <Stack.Screen name="List" component={ListPage} />
          <Stack.Screen name="Detail" component={DetailPage} />
          <Stack.Screen name="Edit" component={EditPage} />
        </Stack.Navigator>
      </NavigationContainer>
      </Provider>
    </>
  );
}

export default App;

1 Answers1

0

Your reducer is wrong.

It needs to always return a full state value, including both any fields that are being updated, and fields that are not being updated.

Both of these cases are broken:

    switch (action.type){
        case 'SHOW_DATA':
            return {
                value: action.payload
            }
        case 'SHOW_CARD':
            return [
                state.detail = action.payload
            ]
    }

The first one does return an object, but only a field called value. So, after that action is dispatched, you will completely lose state.detail and state.filterValue.

The second case is even more broken:

  • It's returning an object instead of an array
  • It's trying to mutate state.detail = instead of doing a correct immutable update
  • And it's still only updating one field out of the three

For that matter, I also note that the showCardDetail() action creator is being used by calculating the entire filtered array first, and stuffing that into the action. That goes against how we want people to use Redux. Instead, we recommend putting as much logic as possible in reducers. Or, in the case of filtering, many times you shouldn't even do that logic in a reducer - do filtering while rendering or in a selector instead.

The biggest fix here is that I would strongly recommend that you switch over to writing your Redux logic with our official Redux Toolkit package. It will drastically simplify your code, catch common mistakes, and make all of this much easier. For example, with RTK, you could write the entire reducer as:

const dataSlice = createSlice({
  name: 'data',
  initialState,
  reducers: {
    dataUpdated(state, action) {
      state.value = action.payload;
    }
    cardShown(state, action) {
      // You can filter here, so I'm showing this, but really do filtering
      // in a selector instead
      state.detail = state.detail.filter(d => d.id === action.payload.id)
    }

  }
})
markerikson
  • 63,178
  • 10
  • 141
  • 157