简体   繁体   中英

React Native 0.64.2, React-Redux 7.2.4, cannot get props to work "undefined is not an object"

I am trying to get React-Redux to work in my new React-Native app and am not succeeding. In my attempts to find online help, all of the examples are using Classes for each Component/Screen, but the beginning React Native template app is no longer using Classes. Everything is defined using "const" and the main App function is defined as:

`const App: () => Node = () => {...`

which are all new to me and I'm not sure if it has anything to do with my failures.

I have several Components/Screens all working nicely and do not have errors until I try to implement Redux.

Reducer:

const initState = {
  syncedDate: '01/02/2022'
}
const projectsReducer = (state = initState, action) => {
  switch (action.type) {
    case 'PJS_SET_SYNCEDDATE':
      return {
        ...state,
        syncedDate: action.syncedDate
      }
      break;
    default:
  }
  return state
}
export default projectsReducer;

Action:

export const pjsSetSyncedDate = (syncedDate) => {
  return {
    type: 'PJS_SET_SYNCEDDATE',
    syncedDate
  }
}

Store:

import { createStore, combineReducers } from 'redux';
import projectsReducer from '../reducers/projects';

const rootReducer = combineReducers({
  projects: projectsReducer
});
const configureStore = () => {
  return createStore(rootReducer);
};
export default configureStore;

App:

...

const store = configureStore();

...

const App: () => Node = () => {
  const isDarkMode = useColorScheme() === 'dark';

  const backgroundStyle = {
    backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
  };

  return (
    <Provider store={store}>
      <NavigationContainer>
      <Tab.Navigator

...

        </Tab.Navigator>
      </NavigationContainer>
    </Provider>
  );
};

...

Component:

import React from 'react';
import type { Node } from 'react';
import {
  Text,
  View,
} from 'react-native';
import { connect } from 'react-redux';

export const DetailsScreen = ({ route, navigation }) => {
  const { name } = route.params;
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Details List</Text>
      <Text>Incoming param: {JSON.stringify(name)}</Text>
      <Text>SyncedDate: {this.props.syncedDate}</Text>
    </View>
  );
};
const mapStateToProps = (state) => {
  return {
    syncedDate: state.projects.syncedDate
  }
};

export default ConnectedDetailsScreen = connect(mapStateToProps)(DetailsScreen);

The error occurs in the Text block

"this.props.syncedDate" - undefined is not an object

You are mixing some old implementation with the new one. try to change their step by step:

As a best practice, use payload property to pass your data through your action, so add it in your action.js:

export const pjsSetSyncedDate = (syncedDate) => {
  return {
    type: 'PJS_SET_SYNCEDDATE',
    payload: syncedDate // -----> added here
  }
}

So, change your reducer to get new syncedData with payload:

const initState = {
  syncedDate: '01/02/2022'
}

const projectsReducer = (state = initState, action) => {
  switch (action.type) {
    case 'PJS_SET_SYNCEDDATE':
      return {
        ...state,
        syncedDate: action.payload.syncedDate   // -------> add payload here
      }
      // break; 
    default:
  }
  return state
}

export default projectsReducer;

Note: you don't need the break expression in the switch/case since you returning the result in the case . I commented out this line in the above reducer code.

Now, back to the component DetailsScreen :

import React from 'react';
import { Text,View } from 'react-native';
import { useSelector } from 'react-redux'; // -----> import useSelector instead of connect method

export const DetailsScreen = ({ route, navigation }) => {
  const { name } = route.params;
  
  const syncedData = useSelector(state => state.projects.syncedData)

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Details List</Text>
      <Text>Incoming param: {JSON.stringify(name)}</Text>
      <Text>SyncedDate: {syncedDate}</Text>
    </View>
  );
};


export default DetailsScreen;

Note: useSelector hook will get the state with a selector function. you define your reducer as projects in the combineReducers so your syncedData is in your state.projects

Note: with the above procedure, you don't need to connect your DetailsScreen to the store to get the state. useSelector hook will do that.

Thanx so much for the help, got this part all working correctly now Now I have related question. Component "DetailsScreen" works fine, but this does not. It generates the error "Invalid hook call"

import React from 'react';
import type { Node } from 'react';
import { useSelector } from 'react-redux';

export const authStage_getToken = () => {
    const username = useSelector(state => state.authentication.username);
    const password = 'test';

    const credentials = base64.encode(username + ':' + password);
    console.log('result:', credentials);

    return credentials;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM