I am new to react native , and I am facing a problem with handling props and state ,when i am using redux, I get the data needed for rendering the flat list in the right form but some how the data property inside the flat list only see {this.props.customers} as undefined.
Here is my code:
componentWillMount() {
debugger;
this.props.getCustomers();
debugger;
}
componentDidUpdate(prevProps) {
if (prevProps.customers !== this.props.customers) {
this.props.getCustomers();
}
}
render = () => {
return (
<View>
<FlatList
data={this.props.customers}
renderItem={({ item }) =>
<View style={styles.GridViewContainer}>
<Text style={styles.GridViewTextLayout}>{item.name}</Text>
</View>}
keyExtractor={(x, index) => index}
numColumns={3}
/>
</View>
);
}
}
const mapStateToProps = (state) => {
const customers = state.customers;
console.log(customers);
debugger
return customers;
};
export default connect(mapStateToProps, {
getCustomers
})(CustomersList);
And the getCustomers action :
export const getCustomers = () => {
debugger;
return (dispatch) => {
dispatch(setCustomerLoading)
axios
.get('https://calm-sands-26165.herokuapp.com/api/customers')
.then(res =>
dispatch({
type: GET_CUSTOMERS,
payload: res.data,
})
)
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: null
})
);
};
}
Thanx in advance.
In mapStateToProps you should return an object, not a value. Each entry in that object will be a prop for the component that's being connected to the store.
In your case, this should be the fix:
const mapStateToProps = (state) => {
const customers = state.customers;
return { customers };
};
Related
Faced a problem while using react-native-fbsdk-next.
The first call to Profile.getCurrentProfile on android returns null. Everything works correctly on ios. If authorization is then called again, the user profile is returned correctly.
If you log out and then try to log in, the first attempt will return null.
Please tell me how to fix this?
const platformPermissions =
Platform.OS === 'ios'
? [['public_profile', 'email'], 'limited', 'my_nonce']
: [['public_profile', 'email']];
const accessToken = yield LoginManager.logInWithPermissions(
...platformPermissions,
).then(
async response => {
if (response.isCancelled) {
return;
}
if (Platform.OS === 'ios') {
const result = await AuthenticationToken.getAuthenticationTokenIOS();
return result?.authenticationToken;
}
if (Platform.OS === 'android') {
const result = await AccessToken.getCurrentAccessToken();
return result?.accessToken;
}
},
error => {},
);
if (accessToken) {
const currentProfile = yield Profile.getCurrentProfile(
profile => profile,
);
if (currentProfile) {
const userData =
Platform.OS === 'ios'
? {
...currentProfile,
firstName: currentProfile.name.split(' ')[0] || null,
lastName: currentProfile.name.split(' ')[1] || null,
}
: currentProfile;
const {data} = yield Axios.post(ROUTES.AUTH.FACEBOOK_SIGN, {
...userData,
accessToken,
});
yield put(setCurrentUser({user: data}));
yield put(setMethod({method: 'facebook'}));
}
}
The issue is in the library itself. PR needs to be created for the solution. Checkout the progress at https://github.com/thebergamo/react-native-fbsdk-next/issues/73
I was facing this issue with the LoginButton. I have no issue when using LoginManager with a custom Button.
My Custom Login button:
const FBLoginButton = () => {
const nav = useNavigation();
return (
<Button
title="Login with Facebook"
onPress={() => {
LoginManager.logInWithPermissions(["public_profile"]).then(
function (result) {
if (result.isCancelled) {
console.log("Login cancelled");
} else {
console.log("Success");
nav.navigate("Home"); // Go to Home screen
}
},
function (error) {
console.log("Login fail with error: " + error);
}
);
}}
/>
);
};
My Home Screen
import { Profile } from "react-native-fbsdk-next";
const HomeScreen = () => {
const [currentUser, setCurrentUser] = useState(null);
const nav = useNavigation();
useEffect(() => {
Profile.getCurrentProfile().then((profile) => {
if (profile) {
setCurrentUser(profile);
}
});
}, []);
return (
<View>
{currentUser ? (
<>
<Text>{`id: ${currentUser.userID}, name: ${currentUser.name}`}</Text>
<Button
title="Logout"
onPress={() => {
LoginManager.logOut();
nav.navigate("Login");
}}
/>
</>
) : (
<Text>Loading...</Text>
)}
</View>
);
};
i'm using react native firebase and i'm getting notification whenever is needed, and these notifications have some data to navigate to specific screen.
i used the firebase documentation to implement the functionality but it's not working as it's supposed to
Here is the document i've used Firebase & React-Navigation
and my code looks something like this :
const Stack = createStackNavigator();
const Router = () => {
const navigation = useNavigation();
const [loading, setLoading] = useState(true);
const [initialRoute, setInitialRoute] = useState('Splash');
useEffect(() => {
//fcm
registerAppWithFCM();
// checkRNFBPermission();
const unsubscribe = messaging().onMessage(async remoteMessage => {
console.log('remote DATAAAAAAAAAAAAAAAAAAAAAAAA : ',remoteMessage.data);
// switch (remoteMessage.data.screen) {
// case 'answer':{
// console.log('inside switch condition 1 !!!!!!!!!!!!!');
// useNavigation().navigate('Profile');
// break;
// }
// case 'AnswerQuestion':{
// console.log('inside switch condition 2 !!!!!!!!!!!!!');
// useNavigation().navigate('Profile');
// break;
// }
// default:
// break;
// }
// Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
// const owner = JSON.parse(remoteMessage.data.owner);
// const user = JSON.parse(remoteMessage.data.user);
// const picture = JSON.parse(remoteMessage.data.picture);
});
// Assume a message-notification contains a "type" property in the data payload of the screen to open
messaging().onNotificationOpenedApp(remoteMessage => {
console.log(
'Notification caused app to open from background state:',
remoteMessage.notification,
);
navigation.navigate('Profile');
});
// Check whether an initial notification is available
messaging()
.getInitialNotification()
.then(remoteMessage => {
if (remoteMessage) {
console.log(
'Notification caused app to open from quit state:',
remoteMessage.data, //notification
);
}
setLoading(false);
});
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Message handled in the background!', remoteMessage);
});
return unsubscribe;
//fcm
}, []);
//fcm
checkRNFBPermission = async() => {
const enabled = await messaging().hasPermission();
if(enabled){
messaging()
.getToken()
.then(token => {
// console.log('deviceeeee fcm token ------> ', token);
});
}else{
requestUserPermission();
}
}
registerAppWithFCM = async() => {
await messaging().registerDeviceForRemoteMessages();
}
requestUserPermission = async() => {
const settings = await messaging().requestPermission();
if (settings) {
console.log('Permission settings:', settings);
}
}
//fcm
renderLoading = () => (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Domanda</Text>
<ActivityIndicator size='large' color={colors.darkerTeal} />
</View>
);
//firebase
if (loading) {
return null;
}
//firebase
return(
<Provider store={store}>
<PersistGate persistor={persistor} loading={this.renderLoading()}>
<Root>
<NavigationContainer>
<Stack.Navigator initialRouteName={initialRoute} headerMode="none">
<Stack.Screen name="Splash" component={Splash} />
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Main" component={Main} />
<Stack.Screen name="AppIntro" component={AppIntro} />
<Stack.Screen name="Tags" component={Tags} />
<Stack.Screen name="Answers" component={Answers} />
<Stack.Screen name="Profile" component={Profile} />
<Stack.Screen name="EditInfo" component={EditInfo} />
<Stack.Screen name="ChangePassword" component={ChangePassword} />
<Stack.Screen name="AnswerQuestion" component={AnswerQuestion} />
<Stack.Screen name="ContactUs" component={ContactUs} />
</Stack.Navigator>
</NavigationContainer>
</Root>
</PersistGate>
</Provider>
)
};
export default Router;
but when i add usenavigation and i want to use it it throws this error:
Error: We couldn't find a navigation object. Is your component inside a screen in a navigator?
i can not use navigation.navigate('Profile'); to navigate to a specific screen.
You're receiving the message in App.js whats outside of your StackNavigator.
You can use a ref to use the navigation property of the navigator
define the navigator in the top of you app.js
var navigator = null;
then add an ref to the navigator
<Stack.Navigator
initialRouteName={initialRoute}
headerMode="none"
ref={nav => {
navigator = nav;
}}
>
and push your route inside the receive method
navigator.dispatch(
NavigationActions.navigate({
routeName: 'theRoute',
params: {},
}),
);
Was struggling 4 hours...
Some were in component, were navigation is available (in my case "Home screen")
// last import
import { ScrollWrapper } from './styles'
export const navigationRef = React.createRef();
export const isReadyRef = React.createRef();
export function navigate(name, params) {
if (isReadyRef.current && navigationRef.current) {
// Perform navigation if the app has mounted
navigationRef.current.navigate(name, params);
} else {
console.log(' else [ELSE] --- ')
// You can decide what to do if the app hasn't mounted
// You can ignore this, or add these actions to a queue you can call later
}
}
// component start
export const SocialHomeScreen = () => {...
In App.js
import { navigate, navigationRef, isReadyRef } from './screens/PeopleAroundYou/index'
// .... navigators
const App = () => {
const [isAuth, setIsAuth] = useState(false)
AsyncStorage.getItem('pushNotify').then(value => {
console.log('value --- ', value)
console.log('JSON.parse(value) --- ', JSON.parse(value))
}).catch(error => {
console.log('error --- ', error)
})
// foreground message arrived
useEffect(() => {
return messaging().onMessage(async remoteMessage => {
const { data, notification } = remoteMessage
if (data.type === 'activity-check-in') {
console.log(' A new FCM message arrived! --- ')
console.log('data --- ', data)
console.log('notification --- ', notification)
console.log(' navigator --- ', navigate)
console.log('navigationRef.current.getRootState() --- ', navigationRef.current.getRootState())
switch (data.category) {
case 'fitness':
// navigate to nested screen
navigate(routes.Fitness, {
screen: routes.ActivityDetails,
params: { activityId: data.eventId}
})
break
case 'companionship':
navigate(routes.Companionships, {
screen: routes.ActivityDetails,
params: { activityId: data.eventId}
})
break
case 'volunteering':
navigate(routes.Volunteering, {
screen: routes.ActivityDetails,
params: { activityId: data.eventId}
})
break
case 'wellbeing':
navigate(routes.Wellbeing, {
screen: routes.ActivityDetails,
params: { activityId: data.eventId}
})
break
}
}
})
}, [])
useEffect(() => {
SplashScreen.hide()
fcmService.registerAppWithFCM()
fcmService.register(onRegister, onNotification, onOpenNotification)
localNotificationService.configure(onOpenNotification)
function onRegister(token) {
console.log('[App] onRegister: ', token)
}
function onNotification(notify) {
console.log('[App] onNotification: ', notify)
const options = {
soundName: 'default',
playSound: true, //,
// largeIcon: 'ic_launcher', // add icon large for Android (Link: app/src/main/mipmap)
// smallIcon: 'ic_launcher' // add icon small for Android (Link: app/src/main/mipmap)
}
localNotificationService.showNotification(
0,
notify.title,
notify.body,
notify,
options,
)
}
function onOpenNotification(notify) {
console.log('[App] onOpenNotification: ', notify)
Alert.alert('Open Notification: ' + notify.body)
}
return () => {
console.log('[App] unRegister')
fcmService.unRegister()
localNotificationService.unregister()
}
}, [])
const authContext = useMemo(() => {
return {
login: () => {
setIsAuth(true)
},
logout: () => {
setIsAuth(false)
},
}
})
return (
<AuthContext.Provider value={authContext}>
<ThemeProvider theme={theme}>
<NavigationContainer
ref={navigationRef}
onReady={() => {
isReadyRef.current = true
}}
linking={linking}
fallback={
<View style={{ justifyContent: 'center', alignItems: 'center' }}>
<Loader loading size='large' color='#61A5C8'/>
</View>
}
>
{isAuth ? <AuthorizedTabs /> : <NonAuthorizedStack/>}
</NavigationContainer>
</ThemeProvider>
</AuthContext.Provider>
)
}
Trying to figure out how params are passed in react-navigation. Once a user selected an option from the Filter using the left header button, loadItems(filter) should be called with that filter as a parameter. How do I catch such event?
export default class FavoritesView extends Component {
static navigationOptions = ({navigation}) => ({
headerLeft: (
<Button
onPress={()=>{FavoritesView.showFilteringMenu(navigation)}}
title="Filter"
/>
),
});
static showFilteringMenu(navigation) {
let FILTERS = [
'A',
'B',
'C'
];
ActionSheet.showActionSheetWithOptions({
title: "Filter options",
options: FILTERS
},
(buttonIndex) => {
navigation.setParams({
selectedOption: FILTERS[buttonIndex]
}); // A parameter is set here
});
}
loadItems(filter) { // This function should be called
StorageService.getItems(filter).then(v => this.setState({ data: v }));
}
render() {
let {navigation} = this.props;
return (
<SafeAreaView style={styles.container}>
<NavigationEvents
onWillFocus={payload => this.loadItems()} // This works only for initial load
/>
</SafeAreaView>
);
}
}
Here is how I solved it using navigation.getParam() and navigation.setParams().
export default class FavoritesView extends Component {
static navigationOptions = ({navigation}) => ({
headerLeft: (
<Button
onPress={navigation.getParam('showFilteringMenu')}
title="Filter"
/>
),
});
static showFilteringMenu() {
let FILTERS = [
'A',
'B',
'C'
];
ActionSheet.showActionSheetWithOptions({
title: "Filter options",
options: FILTERS
},
(buttonIndex) => {
this.selectedFilter = FILTERS[buttonIndex];
this.loadItems(this.selectedFilter);
});
}
componentDidMount() {
this.props.navigation.setParams({
showFilteringMenu: this._showFilteringMenu.bind(this)
});
}
loadItems(filter) { // This function should be called
StorageService.getItems(filter).then(v => this.setState({ data: v }));
}
render() {
let {navigation} = this.props;
return (
<SafeAreaView style={styles.container}>
<NavigationEvents
onWillFocus={payload => this.loadItems()} // This works only for initial load
/>
</SafeAreaView>
);
}
}
I want my apps to display what the user entered in the tag under it with Redux.
So this is my container:
const mapStateToProps = state => ({
text: state
})
const mapDispatchToProps = (dispatch) => ({
addToList: () => { dispatch({ type: 'ADD_LIST' }) },
})
export default connect(mapStateToProps, mapDispatchToProps)(TodoList)
Here is my component:
class TodoList extends Component {
render() {
return (
<View>
<TextInput
style={{height: 40, width: 300}}
placeholder="Type here to translate!"
onChangeText={(text) => this.props.text}
/>
<Button
title="Submit"
onPress={this.props.addToList}/>
<View>
<Text>{this.props.text}</Text>
</View>
</View>
)
}
}
export default TodoList;
Here is the Store:
export const todoList = (state = [], action = {}) => {
switch (action.type) {
case 'ADD_LIST':
return [
...state,
action.todo
];
default:
return state;
}
}
let storeTodoList = createStore(todoList);
export default storeTodoList;
So i'm trying to get the text entered, add it to a list stored in the store and then display it, but i have absolutely no clue how to do this...
You have a few things going on here...
Your onChangeText listener isn't doing anything. You need to capture the text entered into the component and send it to your dispatcher.
You need to include the new text passed in as part of your action creator.
mapStateToProps is responsible for taking the elements in application state and mapping it to the props to be made available to your component. For this example, your application state is pretty simple. It is just { text: 'SOME TEXT' }.
You need to create a Provider for your Redux store. It should work at the root level of your app.
Here are all of the parts:
App.js (the application controller where the Provider is created)
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import todoList from './actions/Reducer';
import { createStore } from 'redux';
import Root from './Root';
class App extends Component {
store = createStore(todoList);
render() {
return (
<Provider store={this.store}>
<Root/>
</Provider>
)
}
}
export default App;
Root.js
import React, { Component } from 'react';
import { View, TextInput, Button, Text } from 'react-native';
import { connect } from 'react-redux';
class Root extends Component {
render() {
const displayText = this.props.textList.join();
return (
<View>
<TextInput
style={{height: 40, width: 300}}
placeholder="Type here to translate!"
onChangeText={(text) => this.props.updateField(text)}
/>
<Button
title="Submit"
onPress={() => this.props.addToList(this.props.text)}/>
<View>
<Text>{displayText}</Text>
</View>
</View>
)
}
}
const mapStateToProps = state => ({
text: state.textField,
textList: state.list
});
const mapDispatchToProps = (dispatch) => ({
updateField: (newText) => { dispatch({ type: 'FIELD_CHANGE', text: newText })},
addToList: (text) => { dispatch({ type: 'ADD_LIST', text: text }) },
});
export default connect(mapStateToProps, mapDispatchToProps)(Root)
Reducer.js (controls your state object)
const INITIAL_STATE = {
textField: '',
list: []
};
export default todoList = (state = INITIAL_STATE, action = {}) => {
switch (action.type) {
case 'FIELD_CHANGE':
return {
...state,
textField: action.text
};
case 'ADD_LIST':
return {
textField: '',
list: [...state.list, action.text]
};
default:
return state;
}
};
EDIT-Changed example to add to a list. Note: This is not the proper way to show a list of items in RN. I'm just throwing the string into a Text field for the example. Use FlatList to properly show a list of items.
Hello fellow programmers, I am having this problem developing this React-Native app where i am rendering a ListView of 'Services' where in each row it has a Text and a Switch, and I am able to render it but when i tap on the row's switch to change the value it goest back to its initial value real fast, I was wondering how to keep this change of vale but since I am new into this I am pretty clueless of how this is done: so far I have the ListView component where I call my ListItem component, heres my code;
class ListView extends Component {
constructor(props) {
super(props);
this.state = {
servicios: []
};
}
componentDidMount() {
AsyncStorage.getItem("token").then((value) => {
axios.get('http://MYURL/api/servicio/index?token=' + value)
.then(response => this.setState({ servicios: response.data.servicios }))
.catch(function (error) {
console.log(error);
});
}).done();
}
renderList() {
console.log('here');
return this.state.servicios.map(servicio =>
<ListItem key={servicio.id} servicio={servicio} />);
}
render() {
const { navigation } = this.props.navigation;
return (
<ScrollView>
{this.renderList()}
</ScrollView>
);
}
}
ListItem.js
const ListItem = ({ servicio }) => {
const { nombre, created_at, estatus } = servicio;
const { thumbnailStyle, headerContentStyle, thumbnailContainerStyle, headerTextStyle, imageStyle } = styles;
return (
<Card>
<CardSection>
<View style={thumbnailContainerStyle}>
<Text style={headerTextStyle}>{nombre}</Text>
</View>
<View style={headerContentStyle}>
<Switch value={estatus}/>
</View>
</CardSection>
</Card>
);
export default ListItem;
I missed the styles to not make this post too long, I may have the clue that i've got to put the current's row switch status in the State but I dont know how to do it, I would be really glad if you guys could help me?
Thanks in advance.
In order to change value of the switch you need to change value in the state from which you're rendering the ListView. I haven't tested that and wrote that from the top of my head, but you should achieve it by introducing small changes here and there:
ListItem.js
const ListItem = ({ servicio, onToggleSwitch }) => {
const { nombre, created_at, estatus, id } = servicio;
const { thumbnailStyle, headerContentStyle, thumbnailContainerStyle, headerTextStyle, imageStyle } = styles;
return (
<Card>
<CardSection>
<View style={thumbnailContainerStyle}>
<Text style={headerTextStyle}>{nombre}</Text>
</View>
<View style={headerContentStyle}>
<Switch value={estatus} onValueChange={(value) => onToggleSwitch(id, value)} />
</View>
</CardSection>
</Card>
);
export default ListItem;
ListView.js
class ListView extends Component {
constructor(props) {
super(props);
this.state = {
servicios: []
};
}
onToggleSwitch = (id, value) => {
const servicios = [...this.state.servicios]
const index = servicios.findIndex(item => item.id === id)
servicios[index].estatus = value
this.setState({ servicios })
}
componentDidMount() {
AsyncStorage.getItem("token").then((value) => {
axios.get('http://MYURL/api/servicio/index?token=' + value)
.then(response => this.setState({ servicios: response.data.servicios }))
.catch(function (error) {
console.log(error);
});
}).done();
}
renderList() {
console.log('here');
return this.state.servicios.map(servicio =>
<ListItem key={servicio.id} servicio={servicio} onToggleSwitch={this.onToggleSwitch} />);
}
render() {
const { navigation } = this.props.navigation;
return (
<ScrollView>
{this.renderList()}
</ScrollView>
);
}
}