ReactNative Context returns undefined when trying to access a value - javascript

I am trying to use the values/functions from my context but every time I console.log a value that Coes from context the value is undefined.
This is my Context:
import { NativeStackNavigationProp } from '#react-navigation/native-stack';
import React, {createContext, FC, useEffect } from 'react';
import { useState } from 'react';
import {fbInit, setNewUserFireStore, subscribeUserFS } from '../services/FirebaseServices';
const initialState = {
signedIn: false,
}
export const FirebaseContext = createContext();
export const FirebaseContextProvider = ({children}) => {
const [isUserSignedIn, setIsUserSignedIn] = useState(initialState.signedIn);
useEffect(() => {
fbInit();
})
const testLog = () => {
console.log("TAG Test Log Func")
}
return (
<FirebaseContext.Provider value={{isUserSignedIn, setIsUserSignedIn}}>
{children}
</FirebaseContext.Provider>
);
}
This is where I am trying to use the values (I am trying to log the values when I press the Button)
import { useNavigation } from '#react-navigation/native';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import {Button, Card, TextInput} from 'react-native-paper';
import { FirebaseContext } from '../../context/FirebaseContext';
import React, {useContext, useEffect, useState } from 'react';
const initialUserState = {
userName: 'Nicole Lopez',
password: '1001008888',
}
export default function LoginScreen() {
const [disabled, setDisabled] = useState(false);
const [userState, setUserState] = useState(initialUserState);
const { fbContext } = useContext(FirebaseContext);
const navigation = useNavigation();
const onInputChange = (field, value) => {
setUserState({
...userState,
[field]: value
})
}
useEffect(() => {
setDisabled(userState.userName.length === 0 || userState.password.length === 0);
}, [userState.userName, userState.password])
return (
<View style={styles.container}>
<Card style={styles.card}>
<TextInput
mode="outlined"
label="Förnamn & Efternman"
defaultValue={userState.userName}
onChangeText={text => onInputChange("username", text)}
right={<TextInput.Icon name="account" onPress={() => {
}}/>}
style={styles.textInput}/>
<TextInput
mode="outlined"
label="Anställningsnummer 100100xxxx"
defaultValue={userState.password}
right={<TextInput.Icon name="lock"/>}
onChangeText={text => onInputChange("password", text)}
style={styles.textInput}/>
</Card>
<View style={styles.buttonRow}>
<Button
color={disabled ? "gray" : undefined}
disabled={disabled}
mode={'contained'}
icon={'login'}
onPress={() => {
console.log("TAG isUserSignedIn: " + fbContext?.isuserSignedIn)
//fbContext?.testLog()
//console.log("TAG LOGIN BTN PRESSED")
//navigation.navigate('HomeScreen')
}}>Login</Button>
</View>
</View>
);
}
This is my App.js:
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import {StackScreens} from './src/helpers/types';
import { NavigationContainer, useNavigationContainerRef } from '#react-navigation/native';
import LoginScreen from './src/screens/LoginScreen/LoginScreen';
import HomeScreen from './src/screens/HomeScreen/HomeScreen';
import {doc, getFirestore, onSnapshot, setDoc, Unsubscribe as UnsubscribeFS} from 'firebase/firestore';
import { FirebaseContextProvider, FirebaseContext } from './src/context/FirebaseContext';
import { useContext } from 'react';
//import { navigationRef } from './RootNavigation';
//const userDocument = firestore().collection("users").doc('Cstl3bA9xwwH3UwHZJhA').get();
const Stack = createNativeStackNavigator();
export default function App() {
return (
<FirebaseContextProvider>
<Content />
</FirebaseContextProvider>
);
}
export const Content = () => {
const navigationRef = useNavigationContainerRef();
const firebaseContext = useContext({FirebaseContext});
return (
<NavigationContainer ref={navigationRef}>
<Stack.Navigator initialRouteName="LoginScreen">
<Stack.Screen name="LoginScreen" component={LoginScreen} />
<Stack.Screen name="HomeScreen" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
Don't know what I have missed, I have installed the packages, I have tried to log different values but all seem to be undefined :/

I believe it is undefined because you're trying to destructure the context value, maybe
const { fbContext } = useContext(FirebaseContext);
should be
const fbContext = useContext(FirebaseContext);

For someone getting undefined from context, check on which level you placed your context provider. If you want to use context in a component you should wrap this component in a context provider outside of this component!
Incorrect way
const Component = () => {
const { value1, value2 } = useContext(MyContext); //here context value will be undefined
return (
<ContextProvider>
//Component content
</ContextProvider>
)
}
Correct way
const ComponentA = () => {
const { value1, value2 } = useContext(MyContext);
return(//Component A content)
}
const ComponentB = () => {
return(
<ContextProvider>
<ComponentA/>
</ContextProvider>
)
}

Related

could not find "store" in the context of "Connect(ListAlarms)" Either wrap the root component in a <Provider>, or pass a custom React context

I am doing an alarm app with react native. When i launch my code it gives me the following error: "could not find "store" in the context of "Connect(ListAlarms)"
Either wrap the root component in a , or pass a custom React context provider to and the corresponding React context consumer to Connect(ListAlarms) in connect options"
what am I doing wrong? what is the solution?
App.js
import React from 'react';
import {
SafeAreaView,
StyleSheet,
Text,
View,
} from 'react-native';
import DateTimePicker from '#react-native-community/datetimepicker';
import ListAlarms from './src/components/ListAlarms';
import TimePicker from './src/components/TimePicker';
const App = () => {
return (
<View style = {styles.mainContainer}>
<Text style={styles.heading}>Alarm App</Text>
<SafeAreaView style={styles.ListAlarms}>
<ListAlarms />
</SafeAreaView>
<View style={styles.TimePicker}>
<TimePicker />
</View>
</View>
);
};
const styles = StyleSheet.create({
mainContainer:{
flex:1,
alignItems: "center",
},
heading:{
fontSize:25,
padding: 20,
},
TimePicker:{
paddingTop:"10%",
width:"50%",
bottom: 20,
},
ListAlarms: {
flex: 1,
width:"100%",
}
});
export default App;
TimePicker.js
import React, { useState } from 'react';
import {
Button,
Alert
} from 'react-native';
import { connect } from 'react-redux';
import { addAlarm } from "../actions/alarms";
import DateTimePicker from 'react-native-modal-datetime-picker';
const TimePicker = (props) => {
const [isDateTimePickerVisible, setIsDateTimePickerVisible] = useState(false);
const makeid=()=>{
var length = 5;
var result = "";
var characters="0123456789";
var charactersLength=characters.length;
for (vari=0; i<length; i++){
result += characters.charAt(Math.floor(Math.random() + charactersLength));
}
return result;
}
const showDateTimePicker = () => {
setIsDateTimePickerVisible(true);
}
const hideDateTimePicker = () => {
setIsDateTimePickerVisible(false);
}
const handleDatePicker = (datetime) => {
var currentTime = Date.now();
if(datetime.getTime() < currentTime) {
Alert.alert("Please Choose future time");
hideDateTimePicker();
return;
}
const alarmNotifData={
id:makeid(),
title: "Alarms Ringing",
message: "My Notification Message",
channel: "alarm-channel",
ticker:"My Notificacion Message",
auto_canel: true,
vibrate:true,
vibration:100,
small_icon:"ic_launcher",
large_icon:"ic_launcher",
play_sound:true,
sound_name: null,
color:"red",
schedule_once:true,
tag:"some_tag",
fire_date:Date.now(),
date:{ value: datetime }
}
props.add(alarmNotifData)
hideDateTimePicker();
}
return(
<>
<Button
title = "+ Add Alarms"
color = "blue"
onPress = {() => {
showDateTimePicker();
}}
>
</Button>
<DateTimePicker
mode="datetime"
isVisible={isDateTimePickerVisible}
onConfirm={handleDatePicker}
onCancel={hideDateTimePicker}
/>
</>
);
}
const mapStateToProps= state =>{
return {};
}
const mapDispatchToProps= dispatch =>{
return {
add:alarmNotifObj=>{
dispatch(addAlarm(alarmNotifObj));
}
};
}
export default connect(mapStateToProps,mapDispatchToProps)(TimePicker);
ListAlarms.js
import React from 'react';
import {
StyleSheet,
Button,
View,
FlatList,
} from 'react-native';
import {ListItem} from 'react-native-elements';
import { connect } from 'react-redux';
import { deleteAlarm } from "../actions/alarms";
import DateTimePicker from '#react-native-community/datetimepicker';
const ListAlarms=(props)=>{
const KeyExtractor =(item,index) => index.toString();
const renderItem = ({item}) => {
return(
<ListItem>
<ListItem.Content>
<ListItem.Title style ={styles.titleStyle}>{item.time.toString()}</ListItem.Title>
<ListItem.Subtitle>{item.date.toString()}</ListItem.Subtitle>
</ListItem.Content>
<Button
title = "Remove"
color = "red"
onPress = {() => {
props.delete(item.value);
}}
>
</Button>
</ListItem>
);
}
return (
<FlatList>
KeyExtracto={KeyExtractor}
data={props.alarms}
renderItem={renderItem}
</FlatList>
);
}
const styles = StyleSheet.create({
titleStyle: { fontWeight: "bold", fontSize: 30 }
})
const mapStateToProps= state =>{
return {
alarms:state.alarms.alarms,
};
}
const mapDispatchToProps= dispatch =>{
return {
delete:value=>{
dispatch(deleteAlarm(value));
}
};
}
export default connect(mapStateToProps,mapDispatchToProps)(ListAlarms);
index.js
import { AppRegistry } from "react-native";
import App from './app.json';
import { name as appName } from './app.json';
import { Provider } from 'react-redux';
import React from 'react';
import configureStore from "./src/store";
const store = configureStore();
const RNRedux=()=>{
return <Provider store={store}>
<App />
</Provider>
}
AppRegistry.registerComponent(appName, () => RNRedux);
**index.js (in store folder)**
import { createStore, combineReducers} from 'redux';
import alarmReducer from '../reducers/alarmReducer';
const rootReducer = combineReducers({
alarms: alarmReducer,
});
const configureStore=()=>{
return createStore(rootReducer);
};
export default configureStore;

React Context returning undefined with useContext

I have context set up within my app. not at the root and for some reason it returns undefined.
I have this as the index where I create the context:
import React, { createContext, useContext, useReducer } from 'react'
type Action = { type: 'next' } | { type: 'back' }
type Dispatch = (action: Action) => void
type State = { step: number }
type StepProviderProps = { children: React.ReactNode }
const StepContext = createContext<{ state: State; dispatch: Dispatch } | undefined>(undefined)
const stepReducer = (state: State, action: Action) => {
switch (action.type) {
case 'next': {
return { step: state.step + 1 }
}
case 'back': {
return { step: state.step - 1 }
}
default: {
throw new Error(`Unhandled action type`)
}
}
}
const StepProvider = ({ children }: StepProviderProps) => {
const [state, dispatch] = useReducer(stepReducer, { step: 0 })
const value = { state, dispatch }
return <StepContext.Provider value={value}>{children}</StepContext.Provider>
}
const useStep = () => {
const context = useContext(StepContext)
if (context === undefined) {
throw new Error('useStep should be used within StepProvider')
} else {
return context
}
}
export { StepProvider, useStep }
then I import it here:
import { BodyText, H3, H5, H4 } from '../../typography'
import * as React from 'react'
import { Box, Icon } from '../../components'
import { useState } from 'react'
import { StyleSheet, TouchableOpacity, View } from 'react-native'
import { useNavigation } from '#react-navigation/native'
import { useAuthContext } from '../../context/AuthProvider'
import { Enum_Userspermissionsuser_Userrole } from '../../generated/graphql'
import Start from './Start'
import stepOne from './StepOne'
import { StepProvider, useStep } from './StepContext'
const OnboardingScreen = () => {
const [stepper, setStepper] = useState([0, 1, 2, 3, 4, 5])
const { navigate } = useNavigation()
const { user } = useAuthContext()
const {state:{step}} = useStep()
return (
<StepProvider>
<Container px={5} justifyContent="flex-start" downbg hasNavbar={false}>
<View style={{ flexDirection: 'row', justifyContent: 'space-evenly', marginTop:'12%'}}>
{stepper.map((step, i) => (
<Box height={5} width={50} bg="b2gpeach" borderRadius={11} key={i} />
))}
</View>
<Start />
{console.log(step)}
</Container>
</StepProvider>
)
}
export default OnboardingScreen
I think I should be able to get console log of the context but I get the context undefined error I set to check if context is defined.
You are calling useStep at the same level as the StepProvider.
You can't use the value from the context in the same component where you've wrapped it.
You need to wrap StepProvider in the parent component then use it:
import { StepProvider} from './StepContext'
const ParenComponent = () => {
<StepProvider>
<OnboardingScreen/>
</StepProvider>
}
import { BodyText, H3, H5, H4 } from '../../typography'
import * as React from 'react'
import { Box, Icon } from '../../components'
import { useState } from 'react'
import { StyleSheet, TouchableOpacity, View } from 'react-native'
import { useNavigation } from '#react-navigation/native'
import { useAuthContext } from '../../context/AuthProvider'
import { Enum_Userspermissionsuser_Userrole } from '../../generated/graphql'
import Start from './Start'
import stepOne from './StepOne'
import { useStep } from './StepContext'
const OnboardingScreen = () => {
const [stepper, setStepper] = useState([0, 1, 2, 3, 4, 5])
const { navigate } = useNavigation()
const { user } = useAuthContext()
const {state:{step}} = useStep()
return (
<Container px={5} justifyContent="flex-start" downbg hasNavbar={false}>
<View style={{ flexDirection: 'row', justifyContent: 'space-evenly', marginTop:'12%'}}>
{stepper.map((step, i) => (
<Box height={5} width={50} bg="b2gpeach" borderRadius={11} key={i} />
))}
</View>
<Start />
{console.log(step)}
</Container>
)
}
export default OnboardingScreen
You must use useContext within components wrapped with provider:
<StepProvider>
<OnboardingScreen />
</StepProvider>
So, you could do something like this:
import { BodyText, H3, H5, H4 } from '../../typography'
import * as React from 'react'
import { Box, Icon } from '../../components'
import { useState } from 'react'
import { StyleSheet, TouchableOpacity, View } from 'react-native'
import { useNavigation } from '#react-navigation/native'
import { useAuthContext } from '../../context/AuthProvider'
import { Enum_Userspermissionsuser_Userrole } from '../../generated/graphql'
import Start from './Start'
import stepOne from './StepOne'
import { StepProvider, useStep } from './StepContext'
const OnboardingScreen = () => {
const [stepper, setStepper] = useState([0, 1, 2, 3, 4, 5])
const { navigate } = useNavigation()
const { user } = useAuthContext()
const {state:{step}} = useStep()
// Provider removed
return (
<Container px={5} justifyContent="flex-start" downbg hasNavbar={false}>
<View style={{ flexDirection: 'row', justifyContent: 'space-evenly', marginTop:'12%'}}>
{stepper.map((step, i) => (
<Box height={5} width={50} bg="b2gpeach" borderRadius={11} key={i} />
))}
</View>
<Start />
{console.log(step)}
</Container>
)
}
// Export wrapped with provider
export default () => (
<StepProvider>
<OnboardingScreen />
</StepProvider>
)
what worked for me was declaring the types. Make use of the following logic:
My AuthContext.tsx
...
1. interface contextValueTypes {
login: Function,
logout: Function,
isLoading: boolean,
userToken: string,
}
2. export const AuthContext = createContext<contextValueTypes>({
login: Function,
logout: Function,
isLoading: false,
userToken: "",
});
...
And in my App.js
const App = () => {
const { isLoading, userToken } = useContext(AuthContext);
return (
<AuthProvider>
{userToken == null ? <AppStack /> : <AuthStack />}
</AuthProvider>
);
}
I hope this helps ya

pass multiple methods from parent to child component in react

I have created Table component. In this component i have created two buttons. one for download and second is share. both have onPress method. I have imported this Table component in the dashboard component. but I am unable to use both methods individually in my dashboard component.please suggest any solution for this problem.
Table Component:
import React, { StrictMode, useEffect, useState } from "react";
import { Text, View, ActivityIndicator } from "react-native";
import Size from "../../constants/Sizes";
import Strings from "../../constants/Strings";
import { Table, TableWrapper, Row, Rows } from "react-native-table-component";
import Color from "../../constants/Colors";
import Icon from "../../styles/Icons";
import api from "../../services/api";
import ListModel from "../ListModal";
import { TableTwoStyle as tableStyle } from "../../styles";
import { heightToDp } from "../../constants/Utils";
const TableTwo = (props) => {
const [files, setFiles] = useState([]);
const [modalState, setModalState] = useState(false);
useEffect(() => {
const fileData = api.getFileOptions();
setFiles(fileData);
}, []);
const { data } = props;
const handleOptions = (title) => {
console.log("title", title);
props.onPress(title);
// this.closeModal();
};
const openModal = () => {
setModalState(true);
};
const closeModal = () => {
setModalState(false);
};
return (
<StrictMode>
{data !== undefined ? (
<View style={tableStyle.mainContainer}>
<View style={tableStyle.HeadingSection}>
<View style={tableStyle.LabelContainer}>
<View style={tableStyle.leftSection}>
<Text style={tableStyle.labelText}>{Strings.tableTitle}</Text>
</View>
<View style={tableStyle.rightSection}>
<Icon.MaterialIcons
name="file-download"
color={Color.gray}
style={tableStyle.exportButton}
size={heightToDp(Size.per_4_5)}
onPress={openModal}
/>
</View>
<View style={tableStyle.rightSection}>
<Icon.MaterialIcons
name="share"
color={Color.info}
style={tableStyle.exportButton}
size={heightToDp(Size.per_4)}
onPress={openModal}
/>
</View>
</View>
<View style={tableStyle.divider} />
</View>
<View style={tableStyle.TableSection}>
{data.headers && data.headers.length > 0 ? (
<Table
borderStyle={{
borderWidth: Size.px_1,
borderColor: Color.dividerColor,
}}
>
<Row
data={data.headers}
flexArr={[Size.num_1]}
style={tableStyle.head}
textStyle={tableStyle.headerText}
/>
<TableWrapper style={tableStyle.wrapper}>
<Rows
data={data.data}
flexArr={[Size.num_1]}
style={tableStyle.row}
textStyle={tableStyle.text}
/>
</TableWrapper>
</Table>
) : (
<ActivityIndicator color={Color.loaderColor} size={Strings.lg} />
)}
</View>
<ListModel
modalState={modalState}
close={closeModal}
onPress={handleOptions}
data={files}
/>
</View>
) : null}
</StrictMode>
);
};
export default TableTwo;
Dashboard Component:
import React, { StrictMode, Component } from "react";
import { View, ScrollView } from "react-native";
import { GraphCardList as GraphList } from "../components";
import { InfoCardList as InfoList } from "../components";
import { TableTwo as Table } from "../components";
import Loader from "../components/Loader";
import Store from "../database/Storage";
import OptionsCard from "../components/Option";
import { Card as CardUI } from "../components";
import { dashboardStyle as dashboardUI } from "../styles";
import Api from "../services/api";
import inputValidation from "../helper/Validation";
import TableExport from "../exports/TableExport";
import Permission from "../services/AppPermission";
export default class DashboardScreen extends Component {
constructor(props) {
super(props);
this.state = {
tableList: [],
};
this.downloadData = this.downloadData.bind(this);
}
componentDidMount() {
}
componentWillUnmount() {
}
downloadData(title) {
...
}
shareData(){
....
}
render() {
const {
loader2,
infoList,
chartList,
tableList,
userList,
loader,
pauseState,
} = this.state;
//console.log("users",infoList);
if (loader) {
return (
<View style={dashboardUI.mainContainer}>
<Loader />
</View>
);
}
return (
<StrictMode>
<CardUI style={dashboardUI.Cards}>
<Table data={tableList} onPress={this.downloadData} />
</CardUI>
)}
</StrictMode>
);
}
}
If your'e passing same prop for two different buttons that means that the two buttons will execute the same method , but if you want to pass different methods for each button just pass a different props.
For example component B:
<View>
<Button title="Download" onPress={props.download}/>
<Button title="Share" onPress={props.share}/>
</View>
Component A:
<B download={this.downloadData} share={this.shareData}/>
Please try below code:
**In Dashboard Component:**
downloadData() {
}
`return (<Table data={tableList} handleDownloadData={this.downloadData} />);`
**In Table Component:**
const download = (data) => {
props.handleDownloadData(data);
};
const share = (data) => {
props.handleDownloadData(data);
};`
`return (<div><Button onClick={download}> Download</Button><Button onClick={share}> Share</Button></div>);
`

Not sure how to integrate function within useEffect

I am trying to call a function within useEffect, how would I mount this function within useEffect, I am not sure?
Initially useEffect is included as the following-
useEffect(() => {
CognitensorEndpoints.getDashboardList({
dispatchReducer: dispatchDashboards,
});
CognitensorEndpoints.getList({
dispatchReducer: dispatchDashboards,
});
},[]);
Then when I include function within useEffect, it returns error - 'Cannot read property 'filter' of undefined' -
useEffect(() => {
console.log('abcd');
setLoading();
CognitensorEndpoints.getDashboardList({
dispatchReducer: dispatchDashboards,
});
CognitensorEndpoints.getList({
dispatchReducer: dispatchDashboards,
});
},[]);
How do I correctly include the 'setLoading()' function within useEffect?
Here's the full app code-
import React, { useState, useEffect, useReducer } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, Keyboard, Button } from 'react-native';
import { Searchbar } from 'react-native-paper';
import { theme } from '../theme';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { apiStateReducer } from '../reducers/ApiStateReducer';
import CognitensorEndpoints from '../services/network/CognitensorEndpoints';
import DefaultView from '../components/default/DefaultView';
import DashboardListCard from '../components/DashboardListCard';
import DashboardHeader from '../components/DashboardHeader';
import DashboardGridCard from '../components/DashboardGridCard';
import {
NavigationContainer,
useFocusEffect,
} from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const AppHeader = ({
scene,
previous,
navigation,
searchIconVisible = false,
item,
index,
onPress
}) => {
const [dashboards, dispatchDashboards] = useReducer(apiStateReducer, {
data: [],
isLoading: true,
isError: false,
});
const [gridView, setGridView] = useState(false);
const toggleGridView = () => {
setGridView(!gridView);
};
const [filtered, setFiltered] = useState([]);
const setLoading = () => {
const messages = dashboards.data.message.filter((item) => {
const title = item.dashboardTitle || item.dashboardName;
return title.toLowerCase();
});
setFiltered(messages);
console.log(filtered);
};
const dropShadowStyle = styles.dropShadow;
const toggleSearchVisibility = () => {
navigation.navigate('Search');
};
useEffect(() => {
console.log(abcd);
setLoading();
CognitensorEndpoints.getDashboardList({
dispatchReducer: dispatchDashboards,
});
CognitensorEndpoints.getList({
dispatchReducer: dispatchDashboards,
});
},[]);
return (
<>
<View style={styles.header}>
<View style={styles.headerLeftIcon}>
<TouchableOpacity onPress={navigation.pop}>
{previous ? (
<MaterialIcons
name="chevron-left"
size={24}
style={styles.visible}
/>
) : (
<MaterialIcons
name="chevron-left"
size={24}
style={styles.invisible}
/>
)}
</TouchableOpacity>
</View>
{filtered.map(item => (
<Text style={styles.headerText}>
{item.dashboardTitle}
</Text>
))}
<View style={styles.headerRightIconContainer}>
{searchIconVisible ? (
<TouchableOpacity
style={[styles.headerRightIcon, dropShadowStyle]}
onPress={toggleSearchVisibility}>
<MaterialIcons name="search" size={24} style={styles.visible} />
</TouchableOpacity>
) : (
<View style={styles.invisible} />
)}
</View>
</View>
</>
);
};
Use setfilters in useEffect. it will be called when dashboards value changed.
useEffect(() => {
console.log(dashboards);
if(dashboards.data.message) {
const messages = dashboards.data.message.filter((item) => {
const title = item.dashboardTitle || item.dashboardName;
return title.toLowerCase();
});
setFiltered(messages);
console.log(filtered);
}
},[dashboards]);
import React, { useState, useEffect, useReducer } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, Keyboard, Button } from 'react-native';
import { Searchbar } from 'react-native-paper';
import { theme } from '../theme';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { apiStateReducer } from '../reducers/ApiStateReducer';
import CognitensorEndpoints from '../services/network/CognitensorEndpoints';
import DefaultView from '../components/default/DefaultView';
import DashboardListCard from '../components/DashboardListCard';
import DashboardHeader from '../components/DashboardHeader';
import DashboardGridCard from '../components/DashboardGridCard';
import {
NavigationContainer,
useFocusEffect,
} from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const AppHeader = ({
scene,
previous,
navigation,
searchIconVisible = false,
item,
index,
onPress
}) => {
const [dashboards, dispatchDashboards] = useReducer(apiStateReducer, {
data: [],
isLoading: true,
isError: false,
});
const [gridView, setGridView] = useState(false);
const toggleGridView = () => {
setGridView(!gridView);
};
const [filtered, setFiltered] = useState([]);
const setLoading = () => {
const messages = dashboards.data.message.filter((item) => {
const title = item.dashboardTitle || item.dashboardName;
return title.toLowerCase();
});
setFiltered(messages);
console.log(filtered);
};
const dropShadowStyle = styles.dropShadow;
const toggleSearchVisibility = () => {
navigation.navigate('Search');
};
useEffect(() => {
CognitensorEndpoints.getDashboardList({
dispatchReducer: dispatchDashboards,
});
CognitensorEndpoints.getList({
dispatchReducer: dispatchDashboards,
});
},[]);
useEffect(()={ if(dashboards.data.length > 0) { setLoading(); } },[dashboards]);
return (
<>
<View style={styles.header}>
<View style={styles.headerLeftIcon}>
<TouchableOpacity onPress={navigation.pop}>
{previous ? (
<MaterialIcons
name="chevron-left"
size={24}
style={styles.visible}
/>
) : (
<MaterialIcons
name="chevron-left"
size={24}
style={styles.invisible}
/>
)}
</TouchableOpacity>
</View>
{filtered.map(item => (
<Text style={styles.headerText}>
{item.dashboardTitle}
</Text>
))}
<View style={styles.headerRightIconContainer}>
{searchIconVisible ? (
<TouchableOpacity
style={[styles.headerRightIcon, dropShadowStyle]}
onPress={toggleSearchVisibility}>
<MaterialIcons name="search" size={24} style={styles.visible} />
</TouchableOpacity>
) : (
<View style={styles.invisible} />
)}
</View>
</View>
</>
);
};

Redux connect can't reach store

I am developing a react-native application using redux. This is my app.js:
import 'react-native-gesture-handler';
import React from 'react';
import {Provider} from 'react-redux';
import AsyncStorage from '#react-native-community/async-storage';
import {DefaultTheme, Provider as PaperProvider} from 'react-native-paper';
import {NavigationContainer} from '#react-navigation/native';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import Icon from 'react-native-vector-icons/Ionicons';
import AppHeader from './components/ui/AppHeader';
import Home from './views/Home';
import Login from './views/Login';
import MyCoupons from './views/MyCoupons';
import ShoppingCart from './views/ShoppingCart';
import Signup from './views/Signup';
import Intro from './views/Intro';
import reducers from './reducers/index';
import configureStore from './store';
export const store = configureStore(reducers);
const Tab = createBottomTabNavigator();
const App = () => {
let localStorageUser = null;
const getLocalStorageUser = async () => {
try {
localStorageUser = await AsyncStorage.getItem('user');
} catch (error) {
console.log(error);
}
};
const getInitialRouteName = () => {
switch(localStorageUser) {
case null: return 'Cuenta'
case '': return 'Cuenta'
default: return 'Home'
}
}
return (
<>
<AppHeader />
<Provider store={store}>
<PaperProvider>
<NavigationContainer>
<Tab.Navigator
initialRouteName={getInitialRouteName()}
screenOptions={({route}) => ({
tabBarIcon: ({focused, color, size}) => {
let iconName;
if (route.name == 'Home') {
iconName = focused? 'home': 'home-outline';
} else if (route.name == 'Mis Cupones') {
iconName = focused? 'film': 'film-outline';
} else if (route.name == 'Carrito') {
iconName = focused? 'cart': 'cart-outline';
} else if (route.name == 'Cuenta') {
iconName = focused? 'person-circle': 'person-circle-outline';
}
return <Icon name={iconName} color='blue' size={25} />
}
})}
>
<Tab.Screen name='Home' component={Home} />
<Tab.Screen name='Carrito' component={ShoppingCart} />
<Tab.Screen name='Mis Cupones' component={MyCoupons} />
<Tab.Screen name='Cuenta' component={Login}/>
</Tab.Navigator>
</NavigationContainer>
</PaperProvider>
</Provider>
</>
);
};
export default App;
This is my store/index.js:
import {applyMiddleware, compose, createStore} from 'redux';
import reducers from '../reducers/index';
import {routerMiddleware} from 'connected-react-router';
import createSagaMiddleware from 'redux-saga';
import rootSaga from '../sagas/index';
import {Provider} from 'react-redux';
import AsyncStorage from '#react-native-community/async-storage';
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default function configureStore(initialState) {
const store = createStore(initialState,
composeEnhancers(applyMiddleware(...middlewares)));
sagaMiddleware.run(rootSaga);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers/index', () => {
const nextRootReducer = require('../reducers/index');
store.replaceReducer(nextRootReducer);
});
}
return store;
}
This is my reducers/index.js:
import {combineReducers} from 'redux';
import Auth from './Auth';
export default () => combineReducers({
auth: Auth,
});
This is my reducers/Auth.js:
import {
GET_USER_SUCCESS,
LOGIN_SUCCESS,
SET_USER,
} from '../types';
const INIT_STATE = {
username: null,
token: null,
userInfo: null,
};
export default (state = INIT_STATE, action) => {
switch (action.type) {
case LOGIN_SUCCESS: {
return {
...state,
username: action.payload.username,
token: action.payload.token
}
}
case GET_USER_SUCCESS: {
return {
...state,
userInfo: Object.assign({}, action.payload.userInfo)
}
}
case SET_USER: {
return {
...state,
username: null,
token: null,
userInfo: null,
}
}
default:
return state
}
}
And this is views/Login.js, where I try to connect to the store:
import React from 'react';
import {View, StyleSheet} from 'react-native';
import {Button, TextInput, Headline} from 'react-native-paper';
import globalStyles from '../styles/global';
import {connect} from 'react-redux';
import AsyncStorage from '#react-native-community/async-storage';
import {login} from '../actions/Auth';
class Login extends React.Component {
constructor (props) {
super();
this.state = {
email: '',
password: '',
loggedIn: false
};
}
render () {
const setLocalStorageUser = async (user) => {
try {
await AsyncStorage.setItem('user', user);
} catch (error) {
console.log(error);
}
};
const handleNewUserPress = () => {
navigation.navigate('Signup');
}
const handleLoginPress = async () => {
login(this.state.email, this.state.password);
navigation.navigate('Home');
}
const handleChange = (name, text) => {
this.setState({[name]: value});
}
return (
<View style={globalStyles.container}>
<TextInput style={styles.input} value={this.state.email} label="Email" onChangeText={(text) => handleChange('email', text)} />
<TextInput style={styles.input} value={this.state.password} label="Contraseña" onChangeText={(text) => handleChange('password', text)} />
<Button
style={styles.button}
mode='contained'
onPress={() => handleLoginPress()}
disabled={this.state.email=='' || this.state.password==''}
>
Enviar
</Button>
<Button color='blue' icon="plus-circle" onPress={() => handleNewUserPress()}>
Nuevo Usuario
</Button>
</View>
);
}
}
const styles = StyleSheet.create({
input: {
marginBottom: 20,
backgroundColor: 'darkgray'
},
button: {
color: 'white',
backgroundColor: 'blue',
marginBottom: 20
}
})
const mapStateToProps = ({auth}) => {
const {username, token} = auth;
return {username, token};
}
const mapDispatchToProps = {
login,
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
The problem is that this arrange is raising this exceptio:
I have checked the code several times and I haven't been able to find my mistake.
Your issue is in here:
export default () => combineReducers({
auth: Auth,
});
now your reducers you supply to createStore is a function ... not the combined-reducers
This should be
export default combineReducers({
auth: Auth,
});

Categories