Ataomega Ataomega - 4 years ago 217
React JSX Question

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>
);
}
}





enter image description here

Answer Source

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":
    }
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download