Apollo refetch not rerendering component - reactjs

I'm fetching data from a web service using graphql my client code is this
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
ActivityIndicator,
View,
FlatList,
TouchableHighlight,
TextInput,
Button,
} from 'react-native';
import PropTypes from 'prop-types';
import { graphql, compose } from 'react-apollo';
import ZONES_QUERY from '../graphql/zones.query'
class ZonesScreen extends Component {
render() {
const { zones, loading, error } = this.props;
if (loading) {
return (
<ActivityIndicator style={styles.activityIndicator} size='large' />
)
} else if (error) {
return (
<View style={styles.container}>
<Text>Unexcpeted Error</Text>
<Button title="retry" onPress={() => { this.props.refetch() }}></Button>
</View>
)
} else {
return (
<View
style={styles.container}>
<FlatList
data={zones}
renderItem={({ item }) => ZoneRenderItem(item)}
keyExtractor={this.keyExtractor}
/>
</View>
)
}
}
//HELPER FUNCTIONS
keyExtractor = item => item._id;
}
ZonesScreen.propTypes = {
refetch: PropTypes.func,
}
const zonesQuery = graphql(ZONES_QUERY, {
options: {
forceFetch: true,
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true,
},
props: ({ data: { loading, getRoutes, error, refetch } }) => ({
loading,
zones: getRoutes,
refetch,
error,
}),
})
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#eee',
},
activityIndicator: {
flex: 1,
justifyContent: 'center',
},
});
export default compose(
zonesQuery,
)(ZonesScreen)
refetch is not working, when I press retry button it gets the response but component is not being reloaded.
Zones is a list of zones that I get from the service.
As you can see I have set three options to the query
forceFetch: true,
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true,
all this read from github repository
https://github.com/apollographql/apollo-client/issues/1622
UPDATE!!!!!!!
I found a workaround using Actions from 'react-native-router-flux' basically the trick is force the rendering of component using Actions.refresh an pass props as parameters to reload them. see function refetch = () => { . I also create an refetching state that tells if data is refetching.
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
ActivityIndicator,
View,
FlatList,
TouchableHighlight,
TextInput,
Button,
} from 'react-native';
import PropTypes from 'prop-types';
import { graphql, compose } from 'react-apollo';
import ZONES_QUERY from '../graphql/zones.query
class ZonesScreen extends Component {
constructor(props) {
super(props)
this.state = { refetching: false }
}
render() {
const { zones, loading, error } = this.props;
const { refetching } = this.state
if (loading || refetching) {
return (
<ActivityIndicator style={styles.activityIndicator} size='large' />
)
} else if (error) {
return (
<View style={styles.container}>
<Text>Unexcpeted Error</Text>
<Button title="retry" onPress={() => { this.refetch() }}></Button>
</View>
)
} else {
return (
<View
style={styles.container}>
<FlatList
data={zones}
renderItem={({ item }) => ZoneRenderItem(item)}
keyExtractor={this.keyExtractor}
/>
</View>
)
}
}
//HELPER FUNCTIONS
keyExtractor = item => item._id;
refetch = () => {
this.setState({ refetching: true });
this.props.refetch()
.then(() => { this.refreshComponent(false, this.props)
})
.catch(error => { this.refreshComponent(false, this.props);
})
}
refreshComponent = (refetching, props) => {
this.setState({ refetching: refetching });
Actions.refresh(props)
}
}
const zonesQuery = graphql(ZONES_QUERY, {
options:  {
errorPolicy: 'ignore',
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true,
},
props: ({ data: { loading, getRoutes, error, refetch, } }) => ({
loading,
zones: getRoutes,
error,
refetch,
}),
})
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#eee',
},
activityIndicator: {
flex: 1,
justifyContent: 'center',
},
});
export default compose(
zonesQuery,
)(ZonesScreen)

Related

Problems with AsyncStorage in React Native

I am building my first React Native app. I am in the process of making the automatic login with AsyncStorage. I already got the login page figured out and I managed to store the user in AsyncStorage. But in the
App.js
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import Login from './components/login'
import Signup from './components/signup'
import Home from './components/home'
import Profile from './components/profile';
const getData = async (user) => {
try {
const value = await AsyncStorage.getItem('#userauthkey')
if (value !== null) {
return value
} else {
return false
}
} catch (e) {
console.log(e)
}
}
const c = async () => {
const ch = await getData('#userauthkey')
return ch
}
console.log(c())
const AppNavigator = createStackNavigator({
Login: {screen: Login},
Signup: {screen: Signup},
Home: {screen: Home},
Profile: {screen: Profile},
})
const AppNavigator2 = createStackNavigator({
Home: {screen: Home},
Login: {screen: Login},
Signup: {screen: Signup},
Home: {screen: Home},
Profile: {screen: Profile},
})
const App = createAppContainer(c() == false ? AppNavigator : AppNavigator2)
export default App;
I get "Error: Objects are not valid as a React child (found: object with keys {_U, _V, _W, _X}). If you meant to render a collection of children, use an array instead." How can I fix it to make it sign the user in automatically?
login screen
import { StatusBar } from 'expo-status-bar'
import React, { useState, useEffect } from 'react'
import { TextInput } from 'react-native-gesture-handler';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { StyleSheet, Text, View, FlatList, Image, Button, Pressable, ScrollView } from 'react-native';
export default function Login(props) {
const message = props.navigation.getParam('message', null)
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const setStringValue = async (value, user) => {
try {
await AsyncStorage.setItem(user, value)
} catch (e) {
console.log(e)
}
}
const getData = async (user) => {
try {
const value = await AsyncStorage.getItem('#userauthkey')
if (value !== null) {
return value
} else {
return false
}
} catch (e) {
console.log(e)
}
}
const log = () => {
fetch(`http://192.168.5.223:8000/home/login/`, {
method: 'POST',
headers: {
"Content-Type": 'application/json'
},
body: JSON.stringify({ username: username, password: password }),
})
.then(res => res.json())
.then(async (res) => {
console.log(res)
if (res.valid === true) {
await setStringValue(username, '#userauthkey')
let ch = await getData(username)
console.log(ch)
if (res.set === true) {
props.navigation.navigate("Home", { "user": username })
} else {
props.navigation.navigate("Profile", { "user": username })
}
} else {
props.navigation.navigate("Login", { 'message': "username/password are incorrect" })
}
})
.catch(error => console.log(error))
}
const sign = () => {
props.navigation.navigate("Signup")
}
return (
<View style={styles.container} >
<ScrollView style={styles.scroll} >
<View style={styles.main}>
<Text style={styles.error}> {message} </Text>
< Text style={styles.label} >
Username:
</Text>
<TextInput style={styles.input} placeholder="Username"
onChangeText={text => setUsername(text)} value={username}
autoCapitalize={'none'}
/>
<Text style={styles.label}> Password: </Text>
<TextInput style={styles.input} placeholder="Password" onChangeText={text => setPassword(text)}
value={password} secureTextEntry={true} autoCapitalize={'none'}
/>
<Button onPress={() => log()} title="Login" > </Button>
</View>
</ScrollView>
< View style={styles.footer} >
<Button onPress={() => sign()} title="Don't have an acount? Sign up now" />
</View>
< StatusBar style="auto" />
</View>
)
}
Login.navigationOptions = screenProps => ({
headerLeft: () => null,
gestureEnabled: false,
headerStyle: {
backgroundColor: 'black'
},
headerTintColor: 'white',
})
const styles = StyleSheet.create({
error: {
textAlign: 'center',
color: 'red',
},
main: {
paddingTop: 145,
paddingHorizontal: 5,
},
container: {
flex: 1,
backgroundColor: 'black',
},
scroll: {
backgroundColor:'#'
},
footer: {
borderTopColor: '#fff',
padding:35,
},
label: {
fontSize: 24,
color: "white",
padding: 10,
},
input: {
fontSize: 24,
backgroundColor: "white",
padding: 10,
margin: 10,
borderRadius: 5,
},
});
Did you imported AsyncStorage in your app.js?
import AsyncStorage from '#react-native-async-storage/async-storage';
You used it but didn't import it, in the code shown. Try to import it and recompile

rangeerror maximum call stack size exceeded in axios GET request

I have very simple component with axios and Material Table. Data from axios request should be loading only once and by clicking refresh button.
import React, { useState, useEffect } from 'react';
import { withStyles } from '#material-ui/core/styles';
import { FormattedMessage, injectIntl } from 'react-intl';
import { Typography, Paper } from '#material-ui/core';
import MaterialTable from 'material-table';
import axios from 'axios';
import { appConfig } from '../../../config';
import drawerStyle from '../../../styles/drawers';
const styles = (theme) => ({
drawerPaper: drawerStyle(theme).drawerSmall,
});
const components = {
Container: ({ children }) => {
return <Paper elevation={0}>{children}</Paper>;
}
};
const DemoRequests = (props) => {
const { intl } = props;
const [requests, setRequests] = useState([]);
const url = `${appConfig.URL_REST}administration/demo_registration_request`;
const config = {
headers: { Authorization: `Bearer ${localStorage.getItem('token')}` },
};
const fetchRequests = () => {
axios.get(url, config)
.then((res) => {
//setRequests(res.data.demoRequests);
console.log(res);
})
//.catch(error => console.log(`Error: ${error}`));
};
useEffect(() => {
fetchRequests();
}, []);
return (
<>
<MaterialTable
title={
<Typography variant="h5" component="span">
<FormattedMessage id="administration.demoRequests.table.title" />
</Typography>
}
columns={[
{
title: <FormattedMessage id="administration.demoRequests.table.id" />,
field: 'id',
},
{
title: <FormattedMessage id="administration.demoRequests.table.company" />,
field: 'company',
},
{
title: <FormattedMessage id="administration.demoRequests.table.email" />,
field: 'email',
}
]}
data={requests}
//components={components}
options={{
padding: 'dense',
searchFieldStyle: { margin: '40px' },
rowStyle: { fontSize: '0.8125rem', padding: '0px' },
cellStyle: {
padding: '2px 16px 1px 16px',
maxHeight: '36px',
},
}}
actions={[
{
icon: 'refresh',
tooltip: intl.formatMessage({
id: 'administration.demoRequests.table.tooltip.refresh.data'
}),
isFreeAction: true,
onClick: () => fetchRequests(),
}
]}
/>
</>
);
}
export default injectIntl(
withStyles(styles, { withTheme: true })(DemoRequests)
);
Uncommenting setRequests(res.data.demoRequests) gives me that stack size exceeded error but I can't see error here. onClick works normally, console.log returns object with data from request. What am I doing wrong here?
Edit:tried to re-npm install, add state to useEffect dependancy array, fetch data inside useEffect
The solution was to install React Developer Tools extension to Chrome browser

How to pass multiple values to react native context API

I'm new to react native concepts. I'm trying to pass functions and a parameter to context API so that I can access those in my child component. I'm trying to implement basic login functionality that will show different messages based on user login status. The functionality works when I pass the SignIn method to my child component but the same function is not accessible when I send it along with some variable. The below code and notes can explain the problem clearly.
In the below code as you can see I'm passing my reducer function and initial states from which I get the error messages and sign function <AuthenticationContext.Provider value={[authContext, initialLoginState]}>
App.js
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import BottomNavigation from './src/Navigations/BottomNavigation';
import AuthStackNavigation from './src/Navigations/AuthStackNavigation'
import { useEffect, useMemo, useReducer } from 'react';
import { View } from 'react-native-animatable';
import apiCaller from "./src/api/apiCaller";
import { ActivityIndicator, Text } from 'react-native';
import { AuthenticationContext } from './src/context/AuthenticationContext'
import { Provider as VideoProvider } from './src/context/videoContext'
import { Context as VideoContext } from './src/context/videoContext'
import AsyncStorage from '#react-native-async-storage/async-storage'
export default function App() {
//Initial state values
const initialLoginState = {
isLoading: true,
userName: null,
userToken: null,
errorMessage: ''
}
//Reducer function
const loginReducer = (prevState, action) => {
switch (action.type) {
case 'LOGIN':
return {
...prevState,
userToken: action.token,
userName: action.id,
isLoading: false
};
case 'LOGOUT':
return {
...prevState,
userName: null,
userToken: null,
isLoading: false,
};
case 'REGISTER':
return {
...prevState,
userToken: action.token,
userName: action.id,
isLoading: false,
};
case 'ERROR':
return {
...prevState,
errorMessage: action.response,
isLoading: false
};
default:
return { userToken: null }
}
}
//Defining useReducer
const [newLoginState, dispatch] = useReducer(loginReducer, initialLoginState);
const authContext = useMemo(() => ({
signIn: async (email, password) => {
try {
const userData = {
email: email,
password: password,
};
const response = await apiCaller.post("/login", userData);
if (response.data.code == '200') {
await AsyncStorage.setItem('userToken', response.data.token)
dispatch({ type: 'LOGIN', id: email, token: response.data.token })
}
else if (response.data.code == '404') {
dispatch({ type: 'ERROR', response: response.data.message })
}
} catch (err) {
console.log(err);
}
}
}), []);
return (
<VideoProvider value={VideoContext}>
<AuthenticationContext.Provider value={{authContext, initialLoginState}}>
<NavigationContainer>
{newLoginState.userToken == null ?
<AuthStackNavigation />
:
<BottomNavigation />
}
</NavigationContainer>
</AuthenticationContext.Provider>
</VideoProvider>
);
}
and in the below file, I'm fetching the provider vales by
const { signIn, initialLoginState } = useContext(AuthenticationContext) but its giving me "signIn is not a function. (In 'signIn(email, password)', 'signIn' is undefined)" error but signIn method is accessible when i just try to pass and access SignIn method alone.
Signinscreen.js
import React, { useState } from 'react'
import { Ionicons, MaterialIcons } from '#expo/vector-icons';
import { Text, View, StyleSheet, TouchableOpacity, Platform, StatusBar } from 'react-native';
import { WindowHeight, WindowWidth } from '../utils/PlatformDimention'
import PrimaryFormInput from "../components/PrimaryFormInput";
import PrimaryFormButton from "../components/PrimaryFormButton";
import { AuthenticationContext } from "../context/AuthenticationContext";
import { useContext } from 'react';
const UserLogin = ({ navigation }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { signIn, initialLoginState } = useContext(AuthenticationContext)
const loginHandle = (email, password) => {
signIn(email, password);
}
return (
<View style={styles.container}>
<StatusBar barStyle='light-content' />
<View style={styles.header}>
<View style={{ flex: 1, }}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<MaterialIcons style={styles.goBack} name="arrow-back" size={WindowHeight / 27} />
</TouchableOpacity>
</View>
<View style={{ flex: 2, alignItems: 'center', justifyContent: 'center', }}>
<Ionicons
style={{ color: "#fff" }} name="logo-bitbucket" size={WindowHeight * 10 / 100} />
</View>
<View style={{ flex: 1, alignItems: 'flex-end' }}>
</View>
</View>
<View style={styles.footer}>
<View style={styles.topFlex}></View>
<View style={styles.middleFlex}>
<Text style={styles.loginText}>Welcome!</Text>
<Text style={styles.loginSubTextSub}>Login to your existing account.</Text>
<PrimaryFormInput
inputValue={email}
onChangeText={(userEmail) => setEmail(userEmail)}
inputPlaceHolder='Email'
iconName="user"
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
/>
<PrimaryFormInput
inputValue={password}
onChangeText={(userPassword) => setPassword(userPassword)}
inputPlaceHolder='Password'
iconName="key"
secureTextEntry={true}
/>
<PrimaryFormButton
ButtonText='Login'
onPress={() => loginHandle(email, password)}
/>
</View>
<View style={styles.bottomFlex}></View>
</View>
</View>
)
}
export default UserLogin
Could anyone of you please let me know how to pass both method and parameters to my child component and access it?
Your mistake is just here
const { signIn, initialLoginState } = useContext(AuthenticationContext)
Because you pass the context object like this
<AuthenticationContext.Provider value={{authContext, initialLoginState}}>
You need to do the destructruring like this
const { authContext : { signIn }, initialLoginState } = useContext(AuthenticationContext)

Rematch for Redux: Struggling to see state on different screens

I am new to Rematch for Redux. I am redoing my application using Rematch, but I do not know if I am doing any of this correctly? How/Where do I trigger my reducers and effects (Step 3 in Rematch Docs)? I cannot see my state when I switch between screens. The state seems to be resetting. Could someone please help me? I would greatly appreciate it.
I followed this: https://github.com/rematch/rematch
Here is my code down below:
My application will get bigger, thus the reason why I placed these in different files to build up into the models.js.
store/user.js
const user = {
state: {},
reducers: {
login(payload) { return payload },
email(state, payload) {
return { ...state, payload }
},
password(state, payload) {
return { ...state, payload }
},
username(state, payload) {
return { ...state, payload }
},
fullname(state, payload) {
return { ...state, payload }
},
}
}
export default user
store/index.js
import { init } from '#rematch/core'
import user from './user'
const models = {
user,
}
const store = init({
models,
})
export default store
App.js
import * as React from 'react';
import SwitchNavigator from './navigation/switchNavigator'
import { Provider } from 'react-redux'
import 'redux'
import store from './store/index'
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<SwitchNavigator/>
</Provider>
);
}
}
navigation/switchNavigator
import * as React from 'react';
import TabNavigatorScreen from './tabNavigator'
import AuthNavigatorScreen from './authNavigator'
import { createAppContainer } from 'react-navigation';
import { createSwitchNavigator } from 'react-navigation-switch-transitioner'
const switchNavigator = createSwitchNavigator(
{
Home: {
screen: TabNavigatorScreen
},
Auth: {
screen: AuthNavigatorScreen
}
},
{
initialRouteName: 'Auth',
}
);
export default createAppContainer(switchNavigator);
navigation/authNavigator
import * as React from 'react';
import LoginScreen from '../screens/login'
import SignUpScreen from '../screens/signup'
import MainScreen from '../screens/main'
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
const authNavigator = createStackNavigator(
{
Main: {
screen: MainScreen,
navigationOptions: {
header: null,
}
},
Login: {
screen: LoginScreen,
navigationOptions: {
title: 'Login',
headerTintColor: '#404040',
headerTitleStyle: {
color: '#404040',
},
headerBackTitleStyle: {
color: '#404040',
},
headerBackTitle: null,
}
},
Signup: {
screen: SignUpScreen,
navigationOptions: {
title: 'Sign Up',
headerTintColor: '#404040',
headerTitleStyle: {
color: '#404040',
},
headerBackTitleStyle: {
color: '#404040',
},
headerBackTitle: null,
}
}
},
{
initialRouteName: 'Main',
}
);
export default createAppContainer(authNavigator);
screens/signUp
import * as React from 'react';
import {
TextInput,
Text,
KeyboardAvoidingView,
SafeAreaView,
TouchableOpacity,
Alert,
}
from 'react-native';
import styles from '../styles'
import { connect } from 'react-redux';
import '#expo/vector-icons';
import 'redux';
class Signup extends React.Component {
render() {
const { routeName } = this.props.navigation.state
return (
<SafeAreaView style={styles.container}>
<KeyboardAvoidingView behavior='position'>
<Text style={styles.mainText}>
EMAIL
</Text>
<TextInput
style={styles.inputText}
editable={routeName === 'Signup' ? true : false}
value={this.props.user.email}
onChangeText={input => this.props.email(input)}
/>
<Text style={styles.mainText}>
PASSWORD
</Text>
<TextInput
style={styles.inputText}
editable={routeName === 'Signup' ? true : false}
value={this.props.user.password}
onChangeText={input => this.props.password(input)}
secureTextEntry={true}
/>
<Text style={styles.mainText}>
USERNAME
</Text>
<TextInput
style={styles.inputText}
value={this.props.user.username}
onChangeText={input => this.props.username(input)}
/>
<Text style={styles.mainText}>
FULL NAME
</Text>
<TextInput
style={styles.inputText}
value={this.props.user.fullname}
onChangeText={input => this.props.fullName(input)}
/>
<TouchableOpacity
style={styles.buttonLighGray}
onPress={() => Alert.alert('Sign up')}>
<Text style={styles.buttonDarkText}>
Accept & Sign Up
</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
}
const mapState = (state) => ({
user: state.user,
})
const mapDispatch = ({ user: { email, password, username, fullname }}) => ({
email: () => email(),
password: () => password(),
username: () => username(),
fullname: () => fullname(),
})
export default connect(mapState, mapDispatch)(Signup)
The Login screen is using the same state as Sign up screen.
The state will never change because you don't pass any parameters while dispatching an action.
const mapDispatch = dispatch => ({
setEmail: mail => dispatch.user.email(mail)
})
Everytime you called a function before you just invoked the reducer without a parameter.
Regarding your rematch model. Your reducers should not just return ...state & payload. Try to
const user = {
state: {},
reducers: {
email(state, email) {
return { ...state, email }
},
password(state, password) {
return { ...state, password }
},
username(state, username) {
return { ...state, username }
},
fullname(state, fullname) {
return { ...state, fullname }
},
}
}

React Native with Redux : data is loaded, render shows empty

I have a react native app with react-redux , redux-persist and redux-thunk.
in the Component, I'm rendering the data from props, if the data length is less than one, i show an error, no data available.
it's always showing 'no data available' but actually data is in the props. as i check the console logs, ( using redux-logger ) data is available in the props.
if i put forceUpdate() at componentDidMount doesnt even help.
but if i put the forceUpdate() with a timeout it will load the data.
setTimeout(()=>{
this.forceUpdate();
}, 1000);
What could be the problem? Is render happening before data loads from props?
CoursesPage.js
import {bindActionCreators} from "redux";
import {connect} from "react-redux";
import Courses from "./components/Courses";
import {Actions as routes} from "react-native-router-flux";
import * as courseActions from "./courses.actions";
function mapStateToProps(state) {
return {
user: state.auth.user,
users: state.auth.users,
courses: state.courses.courses,
lectures: state.courses.lectures,
courseDetails: routes.courseDetails,
openProfile: routes.profilePage
}
}
function dispatchToProps(dispatch) {
return bindActionCreators({
getCourses: courseActions.getCourses
}, dispatch);
}
export default connect(mapStateToProps, dispatchToProps)(Courses);
Courses.js
import React, {Component, PropTypes} from "react";
import {
ActivityIndicator,
ListView,
StyleSheet,
Text,
View,
Image,
NetInfo,
Alert,
TouchableOpacity,
ScrollView,
Dimensions,
Platform,
RefreshControl
} from 'react-native';
import { Loader, Accordion, I18n, CustomNavBar, CustomAccordion } from "../../common/components";
import styles from "../../common/styles";
let DeviceInfo = require('react-native-device-info');
import Icon from 'react-native-vector-icons/Ionicons';
let { width, height } = Dimensions.get('window');
export default class Courses extends Component {
static propTypes = {
user: PropTypes.string.isRequired,
users: PropTypes.object.isRequired,
courseDetails: PropTypes.func.isRequired,
courses: PropTypes.object.isRequired,
getCourses: PropTypes.func.isRequired,
openProfile: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = {
isLoading: false,
isRefreshing: false
};
this._isMounted = false;
}
componentWillMount(){
this._isMounted = true;
const { users, getCourses } = this.props;
getCourses(users);
}
componentWillUnmount(){
this._isMounted = false;
}
componentWillReceiveProps(){
setTimeout(()=>{
this.forceUpdate();
}, 1000);
}
componentDidMount(){
setTimeout(()=>{
this.forceUpdate();
}, 1000);
setTimeout(()=>{
this.forceUpdate();
}, 2000);
}
async loadData(){
await this.props.getCourses(this.props.users);
setTimeout(()=>{
this.forceUpdate();
}, 1000);
}
selectRow(courseData) {
this.props.courseDetails({
courseData: courseData
});
}
renderData(containerList){
/* rendering .... */
}
render() {
const {user, users, getCourses, courses, openProfile} = this.props;
const data = courses[user];
let containerList = [];
Object.keys(data).forEach((d)=>{
let courseList = [];
Object.keys(data[d].courses).forEach((c)=>{
courseList.push(data[d].courses[c]);
});
containerList.push({
id: data[d].id,
title: data[d].title,
courses: courseList
});
});
return (
<View style={styles.container}>
<View style={{ width: width, height: Platform.OS == "ios" ? 64 : 54}}>
<CustomNavBar
width={width}
height={Platform.OS == "ios" ? 64 : 54}
title={I18n.t("details_page_book_button")}
titleSize={18}
buttonSize={15}
background={"#00a2dd"}
color={"#FFF"}
rightIcon={"ios-person-outline"}
rightIconSize={30}
rightAction={()=> { openProfile(); }}
/>
</View>
<View style={{ height: Platform.OS == "ios" ? height - 114 : height - 130 }}>
{!this.state.isLoading ?
<ScrollView
refreshControl={
<RefreshControl
refreshing={this.state.isRefreshing}
onRefresh={this.loadData.bind(this)}
tintColor="#00a2dd"
title=""
titleColor="#00a2dd"
colors={['#00a2dd', '#00a2dd', '#00a2dd']}
progressBackgroundColor="#FFFFFF"
/>
}
>
{this.renderData(containerList)}
</ScrollView>
:<ActivityIndicator
animating={true}
style={{ paddingTop: Platform.OS == "ios" ? (height - 114)/2 : (height - 130)/2 }}
color={'#00a2dd'}
size={'small'}
/>}
</View>
</View>
);
}
}
I think you dont change state so it is seen same data.So I suggest you should change code like following.Also you should immutable js to change state.
courseActions:
export function getCoursesRequest () {
return {
type: "GET_COURSES_REQUEST"
}
}
export function getCoursesSuccess (json) {
return {
type: "GET_COURSES_SUCCESS",
payload: json
}
}
export function getCoursesFailure (json) {
return {
type: "GET_COURSES_FAILURE",
payload: json
}
}
export function getCourses (sessionToken) {
return dispatch => {
dispatch(getCoursesRequest())
// store or get a sessionToken
return appAuthToken.getSessionToken(sessionToken)
.then((token) => {
return BackendFactory(token).getCourses()
})
.then((json) => {
dispatch(getCoursesSuccess(json))
})
.catch((error) => {
dispatch(getCoursesFailure(error))
})
}
}
coursesInitialState
const {Record} = require("immutable");
var InitialState = Record({
courses: {}
});
export default InitialState;
reducer:
const InitialState = require("./authInitialState").default;
const initialState = new InitialState();
export const courseReducer = (state = initialState, action) => {
if (!(state instanceof InitialState)) return initialState.mergeDeep(state);
switch (action.type) {
case "GET_COURSES_SUCCESS":
const {value} = action.payload;
let nextState = state.setIn(["courses"], value;
return nextState;
case "GET_COURSES_FAILURE":
}
}

Resources