I'm building a screen in React Native using expo. I'm new to both React Native and the Rematch framework, and I want to render the first and last names of the basketball players from this endpoint upon load: https://www.balldontlie.io/api/v1/players
Here's my models.js:
import axios from "axios";
export const players = {
state: {
players: [],
},
reducers: {
SET_PLAYERS: (state, payload) => {
return {
...state,
players: payload,
};
},
},
effects: (dispatch) => ({
async getPlayers() {
let response = await axios.get(
"https://www.balldontlie.io/api/v1/players"
);
let { data } = await response.json();
console.log(data);
dispatch.players.SET_PLAYERS(data);
},
}),
};
store.js
import { init } from "#rematch/core";
import * as models from "./models";
const store = init({ models });
export default store;
And finally, my main screen:
import { StatusBar } from "expo-status-bar";
import React from "react";
import { StyleSheet, Text, View } from "react-native";
import { Provider } from "react-redux";
import store from "./state_management/store";
export default function App() {
return (
<View style={styles.container}>
<Players />
<StatusBar style="auto" />
</View>
);
}
const Players = () => {
return (
<Provider store={store}>
// PLAYER LIST HERE!!
</Provider>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
Most examples I have seen online and on here are to do with onPress actions like increments, decrements etc. This involves managing data from an API call so I'm not sure what to do.
In main screen as you are using functional component try using useDispatch and useSelector to dispatch and fetch the list and map it to your screen
import { useDispatch, useSelector } from "react-redux";
const dispatch = useDispatch();
const responseList = useSelector(state => {
return state.apiReducer.data
})
In useEffect dispatch action to models
As from the question what i understand is a need of fetch the stored list from models and render players name, find code for the same in this link
Related
Why my app is only showing loading screen? when i am running my app than its only loading and not showing other screens and showing a WARN that
` WARN Possible Unhandled Promise Rejection (id: 0): TypeError: undefined is
i think it has problem with this code
import React, { useEffect } from 'react';
import { View, ActivityIndicator, StyleSheet} from 'react-native';
import { AsyncStorage } from '#react-native-async-storage/async-storage';
import { useDispatch } from 'react-redux';
import Colors from '../constants/Colors';
import * as authActions from '../store/actions/auth';
const StartupScreen = props => {
const dispatch = useDispatch();
useEffect(() => {
const tryLogin = async () => {
const userData = await AsyncStorage.getItem('userData');
if (!userData) {
// props.navigation.navigate('Auth');
dispatch(authActions.setDidTryAutoLogin());
return;
}
const transformedData = JSON.parse(userData);
const { token, user } = transformedData;
// props.navigation.navigate('Shop');
dispatch(authActions.authenticate(user, token));
};
tryLogin();
}, [dispatch])
return (
<View style={styles.screen}>
<ActivityIndicator size="large" color={Colors.primary} />
</View>
);
};
const styles = StyleSheet.create({
screen: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
});
export default StartupScreen;
According to the docs you import is wrong.
This:
import { AsyncStorage } from '#react-native-async-storage/async-storage';
should be
import AsyncStorage from '#react-native-async-storage/async-storage';
Basically, what you are trying to do with {AsyncStorage} is you are importing "part" of the exported code called AsyncStorage. That is why the error says:
_asyncStorage.AsyncStorage.getItem') (asyncStorage mentioned twice) and you need the entire object out of the package.
Thank you in advance for your time!
As the title describes, I am trying to fetch data from a json file asynchronously and store it in the state with Redux's latest Toolkit version. Unfortunately, running
dispatch(getCities())
Produces Uncaught TypeError: action is undefined
I would appreciate any insight on this! Below goes the code:
STORE.JS
import { configureStore } from "#reduxjs/toolkit";
import citiesReducer from "../features/cities/citiesslice";
const store = configureStore({
reducer:{
cities: citiesReducer
}
})
export default store;
CITIESSLICE.JS
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit"
const API_URL = 'https://warply.s3.amazonaws.com/data/test_pois.json'
export const getCities = createAsyncThunk(
"cities/getCities",
async (dispatch, getState)=>{
return await fetch('https://warply.s3.amazonaws.com/data/test_pois.json')
.then(
console.log('test')
)
}
);
const citiesSlice = createSlice({
name: "city",
initialState: {
cities: [],
loading: 'idle'
},
extraReducers:{
[getCities.pending]: (state,action)=>{
state.loading = "pending"
},
[getCities.fulfilled]: (state, action)=>{
state.loading = "succeded"
state.cities = action.payload
},
[getCities.rejected]: (state, action)=>{
state.loading = "failed"
}
},
})
export default citiesSlice.reducer;
MAPLIST.JS
import React, { useEffect } from 'react';
import { SafeAreaView, StyleSheet, Image, Text, View } from 'react-native';
import getCities from '../features/cities/citiesslice';
import { useDispatch, useSelector } from 'react-redux';
function MapList(props) {
const dispatch = useDispatch();
const {cities}= useSelector(state => state.cities)
useEffect(()=>{
dispatch(getCities())
},[])
return (
<SafeAreaView style={styles.container}>
<View style={styles.topnav}>
<Image source={require('../assets/warply_w.png')} style={styles.logo}/>
</View>
{cities && cities.map((cities, i)=> <Text key={i}>test</Text>)}
</SafeAreaView>
);
Styles not included in the above excerpt as likely not associated with issue
I have a React Native application and it has tabbed layout. I need to call some function when the main screen is loaded. I tried to use componentWillMount() function, but it didn't work because my screen was defined in function and not in class. How can I create on load function?
HomeScreen.js
import React, { useState, Component } from 'react';
import { View, Text } from 'react-native';
import { getText } from '..components/getText';
export default function HomeScreen() {
const [onLoadText, setText] = useState("");
const onScreenLoad = () => {
setText(getText());
}
const componentWillMount = () => {
// Not working
onScreenLoad();
}
return (
<View style={styles.container}>
<Text>{onLoadText}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
since you're using a stateless component, you can use the useEffect hook
useEffect(() => {
// write your code here, it's like componentWillMount
}, [])
You must add useEffect
import React, { useState, Component, useEffect } from 'react';
import { View, Text } from 'react-native';
import { getText } from '..components/getText';
export default function HomeScreen() {
const [onLoadText, setText] = useState("");
const onScreenLoad = () => {
setText(getText());
}
useEffect(() => {
// write your code here, it's like componentWillMount
onScreenLoad();
}, [])
return (
<View style={styles.container}>
<Text>{onLoadText}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
I am trying different ways to display a conditional button based on the athtentication state, but i keep getting into trouble. I have an app.js that defines the stacknavigator, which adds a button to the header giving the option to log out if authenticated.
I wrote a handleLogout function that should perform this.
import React from 'react';
import { Button, Image, View, Text } from 'react-native';
import firebase from 'react-native-firebase';
import Loading from './Loading';
import SignUp from './SignUp';
import Login from './Login';
import Main from './Main';
import {createAppContainer} from 'react-navigation';
import {createStackNavigator} from 'react-navigation-stack';
import { useNavigation } from '#react-navigation/native';
// eslint-disable-next-line no-undef
handleLogOut = () => {
const navigation = useNavigation();
firebase
.auth()
.signOut()
.then(() => this.props.navigation.navigate('Login'))
.catch(error => this.setState({errorMessage: error.message}));
};
const AppNavigator = createStackNavigator(
{
Loading: Loading,
SignUp: SignUp,
Login: Login,
Main: Main,
},
{
initialRouteName: 'Loading',
defaultNavigationOptions: {
headerLeft: null,
headerRight: () => {
let button = this.loggedIn? (
<Button
onPress={this.handleLogOut}
title="Log-out"
color="#fff"
/>
)
:
(
<Button
onPress={() => alert('Please log in')}
title="Log-in"
color="#fff"
/>
)
return button;
},
headerStyle: {
backgroundColor: '#c6f1e7',
},
headerTintColor: '#59616e',
headerTitleStyle: {
fontFamily: 'Raleway-Regular',
fontWeight: '400',
},
},
},
);
export default createAppContainer(AppNavigator);
App.js calls on loading.js where the value for loggedin is declared, based on the authentciation state and then loads either main.js or sign-up. in this case the main page is loaded, which means that someone is authenticated:
// Loading.js
import React from 'react';
import {View, ActivityIndicator, StyleSheet} from 'react-native';
import firebase from 'react-native-firebase';
export default class Loading extends React.Component {
componentDidMount() {
firebase.auth().onAuthStateChanged(user => {
if (user) {
this.setState({ loggedIn: true })
this.props.navigation.navigate(user ? 'Main' : 'SignUp');
} else {
this.setState({ loggedIn: false })
}
});
}
render() {
return (
<View style={styles.container}>
<ActivityIndicator size="large" />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#c6f1e7',
},
});
Now the page redirects to main and shows the welcome message, which indicates that the user is logged in, but the button in the header is saying 'log-in' as well, which means the button is not chosen well. I assume that this is because the loggedin value is not read and it automatically sets it on loggedin: false.
Here is the code for main.js
// Main.js
import React from 'react';
import { View, Text, StyleSheet, Button } from 'react-native';
import firebase from 'react-native-firebase';
import { createAppContainer } from 'react-navigation';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import Kowops from './Kowops';
import Scan from './Scan';
import Wallet from './Wallet';
export class Main extends React.Component {
state = { currentUser: null }
componentDidMount() {
const { currentUser } = firebase.auth()
this.setState({ currentUser })
}
render() {
const { currentUser } = this.state
return (
<View style={styles.container}>
<Text>
Hidiho {currentUser && currentUser.email}!
</Text>
</View>
)
}
}
const bottomTabNavigator = createBottomTabNavigator(
{
Main: {screen: Main},
Kowops: {screen:Kowops},
Scan: {screen:Scan},
Wallet: {screen:Wallet},
},
{
//initialRouteName: 'Main',
tabBarOptions: {
initialRouteName: 'Main',
activeTintColor: '#59616e',
inactiveTintColor: '#a9a9a9',
style: {
backgroundColor: '#c6f1e7',
}
},
});
export default createAppContainer(bottomTabNavigator);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
})
So I need to figure out how to ensure that the value of isloggedin is read properly and the script loads the right button.
Does anyone have a clue?
Thanks in advance!!
Tim
The key here is that you can't use state across different components without passing them as props or through navigation params in this case. You can't use the useNavigation hook outside of a functional component so you should pass the navigation object around when you need it outside of a component (handleLogout is not a component).
Here are some alterations I would make, however I would suggest that you will need to make further changes based on the idea that you can use navigation params to pass information between screens. See more here https://reactnavigation.org/docs/en/params.html.
App.js
DefaultNavigationOptions can be a function which has a navigation prop, this is the navigation object you can use to get params or navigate in the context of the router.
remove that eslint exception because you don't need it, just properly declare the variable. Remove the "this" from you handleLogout function call because it is not a class attribute. Use navigation.getParam to get the isLoggedIn variable which you can pass in the navigate function call.
const handleLogout = navigation => {
firebase
.auth()
.signOut()
.then(() => navigation.navigate('Login'))
.catch(error => this.setState({errorMessage: error.message}));
}
...
defaultNavigationOptions: ({navigation}) => ({
headerRight: () => {
const isLoggedIn = navigation.getParam('isLoggedIn', false);
let button = isLoggedIn ? (
<Button
onPress={() => handleLogOut(navigation)}
title="Log-out"
color="#fff"
/>
) : ...
} ...
Now Loading.js
Here you need to add a navigation param to your navigate call which can then be used in the header
...
firebase.auth().onAuthStateChanged(user => {
if (user) {
this.props.navigation.navigate('Main', {isLoggedIn: true});
} else {
this.props.navigation.navigate('SignUp', {isLoggedIn: false});
}
});
here is a modified version of your code in snack that you can see will get the logged in param and show the logout button https://snack.expo.io/#dannyhw/intrigued-graham-crackers
You will need to make further changes to fix other functionality because I only changed the minimum to show that you can use the navigation param.
I'm getting the following error (articulated above and noted in the screenshot here) for a React Native app. I'm implementing react-navigation into redux.
I also haven't put in any redirects into the app yet. I'm planning on calling NavigationActions to redirect to the Login screen based on the LoggedIn status which is being configured by a separate reducer that manages the state for the user.
The app was working properly when I did not have redux managing the navigation state. I decided to put the navigation state into redux when I realized I'll need some redirects based on the user logged in status which is being managed by the redux.
My code is as follows:
src/navigators/middleware.js
import {
createNavigationPropConstructor,
createReactNavigationReduxMiddleware,
} from 'react-navigation-redux-helpers';
// building redux utils for navigation
export const middleware = createReactNavigationReduxMiddleware(
'root',
state => state.nav,
);
export const navigationPropConstructor = createNavigationPropConstructor('root');
src/navigators/AppNavigator.js
import React from 'react';
import { StackNavigator } from 'react-navigation';
import Welcome from '../screens/Welcome';
import Dashboard from '../screens/Dashboard';
import Login from '../screens/Login';
routeNames = {
Welcome: { screen: Welcome },
Dashboard: { screen: Dashboard },
Login: { screen: Login },
};
config = {
navigationOptions: ({
header: 'null',
headerStyle: {
backgroundColor: 'white',
borderBottomWidth: 0,
},
headerLeft: null,
headerTitleStyle: {
fontSize: 30,
fontFamily: 'Roboto-Bold',
},
}),
};
export const AppNavigator = StackNavigator(routeNames, config);
src/navigators/AppWithInternalState.js
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { addNavigationHelpers } from 'react-navigation';
import { AppNavigator } from './AppNavigator';
import { initializeListeners } from 'react-navigation-redux-helpers';
import { navigationPropConstructor } from './middleware';
class AppWithInternalState extends React.Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
nav: PropTypes.object.isRequired,
};
componentDidMount = () => {
initializeListeners('root', this.props.nav);
};
render = () => {
const { dispatch, nav } = this.props;
const navigation = navigationPropConstructor(dispatch, nav);
return <AppNavigator navigation={ navigation } />;
};
}
const mapStateToProps = state => ({
nav: state.nav,
});
export default connect(mapStateToProps)(AppWithInternalState);
src/reducers/navReducers
import { AppNavigator } from '../navigators/AppNavigator';
import { NavigationActions } from 'react-navigation';
const router = AppNavigator.router;
const firstAction = router.getActionForPathAndParams('Dashboard');
const tempNavState = router.getStateForAction(firstAction);
const secondAction = router.getActionForPathAndParams('Login');
const initialNavState = router.getStateForAction(secondAction, tempNavState);
export default navReducer = (state=initialNavState, action) => {
let nextState;
switch (action.type) {
case 'Login':
nextState = router.getStateForAction(
NavigationActions.back(),
state,
);
break;
default:
nextState = router.getStateForAction(action.state);
break;
}
};
I was able to solve this myself. Looks like there is an issue with react-navigation when using createNavigationPropConstructor.
This will not be solved until react-navigation#2.03 is released.
Until then, create your own navigation prop:
import { createReduxBoundAddListener } from 'react-navigation-redux-helpers';
const navigation = {
dispatch,
state: nav,
addListener: createReduxBoundAddListener('root'),
};
You can see and track the issue here: https://github.com/react-navigation/react-navigation/issues/4320