Unable to Render a FlatList - javascript

I'm trying to render a FlatList but I don't see it on the Screen
I get my Data of my Reducer through a Console Log and in the Console I get the correct Data:
User {
"apellido": "Alas",
"correo": "prueba#uno.com",
"id": 2021-02-08T23:52:58.855Z,
"nombre": "Rene",
"telefono": "72457183",
}
Here is my Code:
import React from "react";
import { View, Text, FlatList, StyleSheet } from "react-native";
import { useSelector } from 'react-redux';
import UserItem from "../components/UserItem";
export default function Page2() {
const users = useSelector(state => state.registro.availableUsers);
console.log(users);
if (users.length === 0) {
return <View style={styles.centered} >
<Text>No Existen Usuarios</Text>
</View>
}
return (
<FlatList
data={users}
keyExtractor={item => item.id.toString()}
renderItem={
itemData => <UserItem
nombre={itemData.item.nombre}
apellido={itemData.item.apellido}
telefono={itemData.item.telefono}
coreo={itemData.item.coreo}
onSelect={() => { }}
/>
}
/>
)
};
const styles = StyleSheet.create({
container: {
backgroundColor: '#f9fafd',
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
}
});
As you can see the Console Log shows there is data being Received from the Reducer, but there is no data displayed through the FlatList
As you can see in the Picture
I tried to get to the same Result (Render a List) with the MAP function but still getting an Error:
TypeError: undefined is not a function (near '...users.map...')
Although I know there shouldn't be an Undefined as there is that printed to the Console
Any Ideas?
PS. I think the Issue might be with the Action or Reducer:
Action
//Import Model
import { createStore } from 'redux';
import User from '../../models/user';
//Constant Creation
export const REGISTER = 'REGISTER';
export const createUser = (nombre, apellido, telefono, correo) => {
return async (dispatch) => {
console.log('entra a fun');
try{
console.log('antes de crear');
const createdUser = new User(new Date(), nombre, apellido, telefono, correo);
console.log('despues de crear');
console.log(createUser);
dispatch({type: REGISTER, user: createdUser});
}
catch (err) {
throw err;
}
}
};
Reducer
import { REGISTER } from '../actions/registro';
const initialState = {
availableUsers: [],
}
export default (state = initialState, action) => {
switch (action.type) {
case REGISTER:
return {
...state,
availableUsers: action.user,
};
}
return state;
};

Your available users is just getting one user, instead of updating/appending the new one.
export default (state = initialState, action) => {
switch (action.type) {
case REGISTER:
return {
...state,
availableUsers: [...state.availableUsers, action.user],
};
}
return state;
};
By spreading (...) state.availableUsers, you can append the new user to the end of the existing array.

Related

useSelector doesn't update value in Next.js

I have a problem, createAsyncThunk function makes request to server (axios) and then get data, after that extraReducers handle builder.addCase in it and makes state.value = action.payload, then console.log(state.value) writes value from server. Great! It works, but when I use useSelector it sees existing value from initialState but get value only when it was first time initialized (null or just []) not updated after dispatch in wrapper.getServerSIdeProps. Same with just reducers and function in it. It works change state (console.log write it) but useSelector doesn't give me updated value.
UPDATE:
If you have same issue.
Just give up, don't use next-redux-wrapper. Context Api.
Slice code
import { createAsyncThunk, createSlice, PayloadAction } from '#reduxjs/toolkit';
import axios from 'axios';
import { HYDRATE } from 'next-redux-wrapper';
// types
import { IInitialStateV1 } from '../types/store';
import { ITrack } from '../types/tracks/track';
export const fetchData = createAsyncThunk('main/fetchData', async (): Promise<ITrack[]> => {
const { data } = await axios.get<ITrack[]>('http://localhost:5000/track');
return data;
})
const initialState: IInitialStateV1 = {
pause: true,
currentTime: 0,
volume: 0,
duration: 0,
active: null,
tracks: [],
}
export const mainSlice = createSlice({
name: 'main',
initialState,
reducers: {
setPause(state, action: PayloadAction<boolean>) {
state.pause = action.payload;
},
setTime(state, action: PayloadAction<number>) {
state.currentTime = action.payload;
},
setVolume(state, action: PayloadAction<number>) {
state.volume = action.payload;
},
setDuration(state, action: PayloadAction<number>) {
state.duration = action.payload;
},
setActive(state, action: PayloadAction<ITrack>) {
state.active = action.payload;
state.currentTime = 0;
state.duration = 0;
}
},
extraReducers: (builder) => {
// [HYDRATE]: (state, action) => {
// return {
// ...state,
// ...action.payload,
// }
// },
// [fetchData.fulfilled.toString()]: (state, action: PayloadAction<ITrack[]>) => {
// state.tracks = action.payload;
// }
builder.addCase(HYDRATE, (state, action: any) => {
return {
...state,
...action.payload,
}
}).addCase(fetchData.fulfilled, (state, action: PayloadAction<ITrack[]>) => {
// return {
// ...state,
// ...action.payload,
// }
state.tracks = action.payload;
});
}
})
export const { setPause, setTime, setVolume, setDuration, setActive } = mainSlice.actions;
export default mainSlice.reducer;
configurate store
import { AnyAction, configureStore, ThunkDispatch } from '#reduxjs/toolkit';
import { createWrapper, MakeStore, Context } from 'next-redux-wrapper';
import mainRed from './index';
const makeStore = () => configureStore({
reducer: {
main: mainRed
},
})
type AppStore = ReturnType<typeof makeStore>;
export type RootState = ReturnType<AppStore['getState']>;
export type AppDispatch = AppStore['dispatch'];
export type NextThunkDispatch = ThunkDispatch<RootState, void, AnyAction>;
export const wrapper = createWrapper<AppStore>(makeStore);
hooks for TypeScript
import { useDispatch, useSelector, TypedUseSelectorHook } from "react-redux";
import { RootState, AppDispatch } from "../store/reducer";
export const useTypeSelector: TypedUseSelectorHook<RootState> = useSelector;
export const useTypeDispath = ()=> useDispatch<AppDispatch>();
getServerSideProps and useSelector (in page)
import { Container, ListItem, Stack, Box, Button } from "#mui/material";
import TrackList from "../../components/TrackList";
// interfaces
import { ITrack } from "../../types/tracks/track";
// import hooks
import { useRouter } from "next/router";
import { useTypeSelector } from "../../hooks/useTypeSelector";
// wrapper
import { NextThunkDispatch, wrapper } from "../../store/reducer";
import { fetchData, setVolume } from "../../store";
export default function Index(): JSX.Element {
const router = useRouter();
const tracks: ITrack[] = useTypeSelector(state => state.main.tracks);
return (
<div className="main">
<Container >
<Stack marginTop={20} sx={{ backgroundColor: "#C4C4C4", fontSize: '24px', fontWeight: 'bold' }}>
<Box p={5} justifyContent="space-between">
<ListItem>List of Tracks</ListItem>
<Button variant="outlined" sx={{ backgroundColor: 'blue', color: 'white' }} onClick={() => router.push('/tracks/create')} >Upload</Button>
</Box>
<TrackList tracks={tracks} />
</Stack>
</Container>
</div>
)
}
export const getServerSideProps = wrapper.getServerSideProps((store) => async () => {
const dispatch = store.dispatch as NextThunkDispatch;
// dispatch(fetchData());
dispatch(setVolume(2));
return {
props: {}
}
})

When I console log redux store data it gets logged 4 times

I am using redux and redux-thunk to store my data from API calls and everything is working fine, but when I get data in a component from my redux store and log it on console it gets logged 4 times, 2 times previous data (empty array) and 2 times new data, I don't know why it is happening and how to resolve this, kindly help.
Here is my component code, I am getting all 250 countries' data in this component and displaying country names and flags. On line 15 console log I get this on the console.
import React from "react";
import { useEffect } from "react";
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { Row, Col, Spinner } from "react-bootstrap";
import { Link } from "react-router-dom";
import { getAllCountries } from "../redux/actions";
const AllCountries = () => {
const dispatch = useDispatch();
const { allCountries, loading } = useSelector(
(state) => state.getCountriesData
);
console.log(allCountries);
useEffect(() => {
dispatch(getAllCountries());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className="center-div">
{loading ? (
<Spinner animation="border" variant="light" className="margin" />
) : (
<Row>
{allCountries.map((country) => {
return (
<Col key={country.name} sm={4}>
<Link
to={`/country/${country.name}`}
style={{ textDecoration: "none" }}>
<div className="my-card zoom">
<img src={country.flag} alt="flag" className="flag" />
<h3 className="white-text">{country.name}</h3>
</div>
</Link>
</Col>
);
})}
</Row>
)}
</div>
);
};
export default AllCountries;
Redux action file from where I am making api call
export const getAllCountries = () => {
return async (dispatch) => {
dispatch(setLoading(true));
const res = await axios.get(`https://restcountries.eu/rest/v2/all`);
dispatch({
type: "GET_ALL_COUNTRIES",
payload: res.data,
});
dispatch(setLoading(false));
};
};
Reducer
const initialState = {
countries: [],
countryData: [],
allCountries: [],
loading: false,
};
const getCountriesData = (state = initialState, action) => {
switch (action.type) {
case "GET_COUNTRIES":
return {
...state,
countries: action.payload,
};
case "GET_ALL_COUNTRIES":
return {
...state,
allCountries: action.payload,
};
case "GET_COUNTRY_DATA":
return {
...state,
countryData: action.payload,
};
case "SET_LOADING":
return {
...state,
loading: action.payload,
};
case "CLEAR_STATE": {
return {
...initialState,
};
}
default:
return state;
}
};
export default getCountriesData;
Store
import { createStore, applyMiddleware, compose } from "redux";
import reducers from "./redux/reducers";
import thunk from "redux-thunk";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducers, composeEnhancers(applyMiddleware(thunk)));
export default store;
This is expected behaviour with your current setup
First log is in the initial render of your component
Second log is after the initial render, useEffect is trigger follow up with dispatch(getAllCountries()) causing dispatch(setLoading(true)) which lead to the 2nd render.
The third log is when your Axios fetching is done and GET_ALL_COUNTRIES is triggered, this changes your state causing 3rd re-render.
The fourth log is after GET_ALL_COUNTRIES is finished and then dispatch(setLoading(false)) is triggered causing loading state change again.
You can reduce the number of re-render by remove dispatch(setLoading(false)) and place it in the reducer.
case "GET_COUNTRIES":
return {
...state,
loading: false,
countries: action.payload,
};
My suggestion with your case is that you should not worry too much about re-render since your component is lightweight and doesn't have any heavy process.

State is not triggering re-render after change in state

I am combining two reducers in my React app. One of them is working fine, but one is not triggering re-render after a change in state. But after when I save document or make change in other reducer state, component re-renders for both reducers
reducer which is not working fine:
import {
SET_ACTIVE_SUBJECT,
SET_ACTIVE_TOPIC
} from '../action/action-types'
const initialState = {
activeSubject: '',
activeTopic: ''
}
const makeState = (stateActiveSubject, stateActiveTopic) => {
return {
activeSubject: stateActiveSubject,
activeTopic: stateActiveTopic
}
}
export const uireducer = (state = initialState, action) => {
switch(action.type){
case SET_ACTIVE_SUBJECT:
// this statement is printing new state in console, but not triggering re-render
console.log('New State : ', makeState(action.payload,''));
return makeState(action.payload,'')
case SET_ACTIVE_TOPIC:
return makeState(state.uireducer.activeSubject,action.payload)
default:
return state
}
}
Component which is not re-rendering:
const Topics = ({activeSubject, data}) => {
const classes = useStyles()
const [topics, setTopics] = useState([])
useEffect(() => {
console.log('Active Subject : ', activeSubject);
if(activeSubject){
console.log('Data : ', data.filter(subject => (subject.id === activeSubject))[0].topics);
setTopics(data.filter(subject => (subject.id === activeSubject))[0].topics)
}
}, [])
return (
<List>
{
topics.length > 0 ? topics.map(topic => {
return (
<ListItem button className={classes.listItem} id={topic.id} key={topic.id}>
{topic.name}
<ButtonGroup className={classes.editDelete}>
<IconButton className={classes.icon}>
<Edit />
</IconButton>
<IconButton className={classes.icon}>
<Delete />
</IconButton>
</ButtonGroup>
</ListItem>
)
}) : <div className={classes.waitMessage}><p>Select Subject To Display Topics</p></div>
}
</List>
)
}
const mapStateToProps = state => ({
activeSubject: state.uireducer.activeSubject,
data: state.subjects.data
})
const mapDispatchToProps = dispatch => ({
})
export default connect(mapStateToProps, mapDispatchToProps)(Topics);
reducer which is working fine:
import {
FETCHING_DATA,
FETCHED_DATA,
FETCH_DATA_ERROR
} from '../action/action-types'
const initialState = {
isDataFetching : false,
error: '',
data: [],
}
const makeState = (dataFetching, stateError, stateData) => {
return {
isDataFetching: dataFetching,
error: stateError,
data: stateData,
}
}
export const reducer = (state = initialState, action) => {
switch(action.type){
case FETCHING_DATA:
return makeState(true,'',[])
case FETCHED_DATA:
return makeState(false,'',action.payload)
case FETCH_DATA_ERROR:
return makeState(false,action.payload,[])
default:
return state
}
}
Store here :
import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk';
import {reducer} from '../reducer/reducer'
import { uireducer } from '../reducer/uireducer'
console.log(reducer);
const rootReducer = combineReducers({
subjects: reducer,
uireducer
})
const store = createStore(rootReducer, applyMiddleware(thunk))
export default store
Set activeSubject as dependency for the useEffect, so it will rerun when the value changes.
useEffect(() => {
// ...
}, [activeSubject])

React Native - Navigating by ID from api with Redux not working

So, i'am using redux for my application and so far it's good. But recently I just hit a wall that stops me from coding for a while. I'am trying to make a navigation using the ID of every item in my database with redux. let's say I have a "category of food" page that is filled with item from my database and each of those items has their own ID, using those ID I'am trying to navigate to a second page which is the "dishes page". If a user clicks a category, the application should display the dishes that the category contains.
As of now, I manage to display the category but I don't know how to apply the navigation.
Whenever I try to add a kind of navigation that I know I'am facing an error:
Error
state_error ( ** UPDATE ** )
You can see all the details in my code.
Here is my code:
App.js ( ** EDIT ** )
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import Reducer from './app/redux/reducers/Reducer';
import AppContainer from './app/container/AppContainer';
import DishContainer from './app/container/DishContainer';
import Dishes from './app/component/Dishes';
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const store = createStoreWithMiddleware(Reducer);
export default class App extends Component{
render() {
return (
<Provider store = { store }>
<Root />
</Provider>
);
}
}
const Root = StackNavigator ({
AppContainer : { screen: AppContainer },
DishContainer : { screen: DishContainer },
Dishes : { screen: Dishes },
})
Action.js
import {
FETCHING_CATEGORY_REQUEST,
FETCHING_CATEGORY_SUCCESS,
FETCHING_CATEGORY_FAILURE,
FETCHING_DISHES_REQUEST,
FETCHING_DISHES_SUCCESS,
FETCHING_DISHES_FAILURE,
} from "./types";
import axios from 'axios';
/* ---------------------- CATEGORY ---------------------------- */
export const fetchingCategoryRequest = () => ({
type: FETCHING_CATEGORY_REQUEST
});
export const fetchingCategorySuccess = (json) => ({
type: FETCHING_CATEGORY_SUCCESS,
payload: json,
});
export const fetchingCategoryFailure = (error) => ({
type: FETCHING_CATEGORY_FAILURE,
payload: error
});
export const fetchCategory = () => {
return (dispatch) => {
axios.get('http://192.168.254.100:3308/categories/')
.then(response => {
dispatch({ type: FETCHING_CATEGORY_SUCCESS, payload: response.data })
})
.catch(error => console.log(error.response.data));
}
}
/* ---------------------- DISHES ---------------------------- */
export const fetchingDishesRequest = () => ({
type: FETCHING_DISHES_REQUEST
});
export const fetchingDishesSuccess = (json) => ({
type: FETCHING_DISHES_SUCCESS,
dish: json,
});
export const fetchingDishesFailure = (error) => ({
type: FETCHING_DISHES_FAILURE,
dish: error,
});
export const fetchDish = () => {
return (dispatch) => {
const { params } = this.props.navigation.state;
axios.get('http://192.168.254.100:3308/categories/' + params.id)
.then(response => {
dispatch({ type: FETCHING_DISHES_SUCCESS, dish: response.data })
})
.catch(error => console.log(error.response.data));
}
}
Reducer.js
const initialState = {
isFetching: false,
errorMessage: '',
category: [],
dishes: [],
};
const categoryReducer = (state = initialState, action) => {
switch(action.type) {
case FETCHING_CATEGORY_REQUEST:
return {
...state,
isFetching: true
};
case FETCHING_CATEGORY_FAILURE:
return {
...state,
isFetching: false,
errorMessage: action.payload
};
case FETCHING_CATEGORY_SUCCESS:
return {
...state,
isFetching: false,
category: action.payload
};
/* --------------------- DISHES ------------------------- */
case FETCHING_DISHES_REQUEST:
return {
...state,
isFetching: true
};
case FETCHING_DISHES_SUCCESS:
return {
...state,
isFetching: false,
dishes: action.dish
};
case FETCHING_DISHES_FAILURE:
return {
...state,
isFetching: false,
errorMessage: action.dish
}
default:
return state;
}
}
export default categoryReducer;
CategoryList.js
export default class CategoryList extends Component {
_renderItem = ({ item }) => {
const { cat_name } = item;
return (
<View style={styles.cardContainerStyle}>
<View style={{ paddingRight: 5 }}>
<TouchableOpacity style = { styles.buttonContainer }>
<Text style={styles.cardTextStyle}
onPress = { () => this.props.navigation.navigate('Dishes', { id: item.cat_id })}>
{cat_name}
</Text>
</TouchableOpacity>
</View>
</View>
);
};
render() {
return (
<FlatList
style={{ flex: 1 }}
data = {this.props.category}
keyExtractor={(item, index) => index.toString()}
renderItem={this._renderItem}
/>
)
}
}
AppContainer.js
import CategoryList from "../component/CategoryList";
import { fetchCategory } from '../redux/actions/Actions';
import { connect } from 'react-redux';
class AppContainer extends Component {
componentDidMount() {
this.props.fetchCategory();
}
render() {
let content = <CategoryList category = { this.props.randomCategory.category }/>;
if (this.props.randomCategory.isFetching) {
content = <ActivityIndicator size="large"/>;
}
return <View style={styles.container}>{content}</View>;
}
}
const mapStateToProps = state => {
return {
randomCategory: state
};
}
export default connect(mapStateToProps, { fetchCategory })(AppContainer);
Dishes.js (This is where I want to navigate from Category)
export default class Dishes extends Component {
_renderItem = ({ item }) => {
const { cat_desc } = item;
return (
<View style={styles.cardContainerStyle}>
<View style={{ paddingRight: 5 }}>
<TouchableOpacity>
<Text style={styles.cardTextStyle}>
{cat_desc}
</Text>
</TouchableOpacity>
</View>
</View>
);
};
render() {
const { params } = this.props.navigation.state;
return (
<FlatList
style={{ flex: 1 }}
data = {this.props.dishes}
keyExtractor={(item, index) => index.toString()}
renderItem={this._renderItem}
/>
)
}
}
DishContainer.js
import Dishes from "../component/Dishes";
import { fetchDish } from '../redux/actions/Actions';
import { connect } from 'react-redux';
class DishContainer extends Component {
componentDidMount() {
this.props.fetchDish();
}
render() {
let content = <Dishes dishes = { this.props.randomDishes.dishes }/>;
if (this.props.randomDishes.isFetching) {
content = <ActivityIndicator size="large"/>;
}
return <View style={styles.container}>{content}</View>;
}
}
const mapStateToProps = state => {
return {
randomDishes: state
};
}
EDIT: Some how the params is not working or the props.navigation.navigate is not calling the item's id from my api. I'am not sure, please help me.
Edit your AppContainer.js like below :
import CategoryList from "../component/CategoryList";
import { fetchCategory } from '../redux/actions/Actions';
import { connect } from 'react-redux';
class AppContainer extends Component {
componentDidMount() {
this.props.fetchCategory();
}
render() {
let content = <CategoryList
navigation = {this.props.navigation}
category = {this.props.randomCategory.category}/>;
if (this.props.randomCategory.isFetching) {
content = <ActivityIndicator size="large"/>;
}
return <View style={styles.container}>{content}</View>;
}
}
const mapStateToProps = state => {
return {
randomCategory: state
};
}
export default connect(mapStateToProps, { fetchCategory })(AppContainer);
The issue is CategoryList can't get navigation props of parent component(AppCountainer), So you have to pass navigation as props.

Unable to fetch data from api using fetch()

I have a react-native project implemented with redux where I am trying to fetch data from an api using fetch.I am unable to fetch the data from the api.Also,it doesn't give any errors. My code is as shown below:
Action.js
//import axios from 'axios';
export const FETCH_DATA = 'fetch_data';
export const FETCH_SUCCESS = 'fetch_success';
export function fetchData() {
return dispatch => {
return(fetch('https://api.myjson.com/bins/fz62x'))
.then(res => res.json())
.then(data => {
dispatch({
type: FETCH_DATA,
payload: data
})
}
)
}
}
I have 2 reducer files:
fetch_data.js
import FETCH_DATA from '../actions'
const initialState = {
result:[],
isFetching: false,
error: false
}
export default function fetchReducer(state = initialState, action) {
switch (action.type) {
case FETCH_DATA: {
return {
...state,
isFetching: true,
result: action.payload.data
}
}
default:
return state
}
}
index.js
import { combineReducers } from 'redux';
import fetchData from './fetch_data';
const rootReducer = combineReducers({
result: fetchData
})
export default rootReducer;
My component where I am trying to access the data fetched from the api:
HomeScreen.js
import React from 'react';
import { StyleSheet,
Text,
View,
ActivityIndicator
} from 'react-native';
import { bindActionCreators } from 'redux';
import reducer from '../reducers/fetch_data';
import thunk from 'redux-thunk';
import { connect } from 'react-redux';
import { fetchData } from '../actions';
class HomeScreen extends React.Component {
static navigationOptions = {
header: null
}
componentDidMount() {
this.props.fetchData()
}
render() {
const { result, isFetching } = this.props.result;
if (isFetching) {
return(
<View style={{flex: 1, flexDirection: 'column', alignItems: 'center', justifyContent: 'center'}}>
<ActivityIndicator size={'large'} />
</View>
)
} else {
return(
<View style={{flex: 1, flexDirection: 'column', alignItems: 'center', justifyContent: 'center'}}>
<Text>{result.length}</Text>
</View>
)
}
}
}
function mapStateToProps(state) {
return {
result: state.result
}
}
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators({ fetchData }, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'blue'
},
welcome: {
color: '#FFF',
fontSize: 30
}
})
So,in my Homescreen.js where I try to ge the result of the api in <Text>{result}</Text> ,I get a blank page. If I try to get the length of the result props,I get 0.What is the issue with this code,can anyone please help me with this?
NOTE: Also, isFetching props is returning the default value,i.e. false, so I am directly navigated to the other page where result prop is mentioned.
EDIT 1 Updated files for the action,reducer and component are given below:
Actions.js
//import axios from 'axios';
//import { FETCH_DATA, FETCH_DATA_SUCCESS, FETCH_DATA_FAILURE } from '/constants';
export const FETCH_DATA = 'FETCH_DATA';
export const FETCH_DATA_SUCCESS = 'FETCH_DATA_SUCCESS';
export const FETCH_DATA_FAILURE = 'FETCH_DATA_FAILURE';
export function fetchData() {
return (dispatch) => {
dispatch(getData())
fetch('https://api.myjson.com/bins/fz62x')
.then(res => res.json())
.then(json => dispatch(getDataSuccess(json.results)))
.catch(err => dispatch(getDataFailure(err)))
}
}
function getData() {
return {
type: FETCH_DATA
}
}
function getDataSuccess(data) {
return {
type: FETCH_DATA_SUCCESS,
data
}
}
function getDataFailure() {
return {
type: FETCH_DATA_FAILURE,
}
}
fetch_data.js
import FETCH_DATA from '../actions';
import FETCH_DATA_SUCCESS from '../actions';
import FETCH_DATA_FAILURE from '../actions';
const initialState = {
result_array:[],
isFetching: false,
error: false
}
export default function fetchReducer(state = initialState, action) {
switch (action.type) {
case FETCH_DATA: {
return {
...state,
isFetching: true,
}
}
case FETCH_DATA_SUCCESS: {
return {
...state,
isFetching: false,
result_array: action.payload
}
}
case FETCH_DATA_FAILURE:
return {
...state,
isFetching: false,
error:true
}
default:
return state
}
}
HomeScreen.js
import React from 'react';
import { StyleSheet,
Text,
View,
ActivityIndicator
} from 'react-native';
import { bindActionCreators } from 'redux';
import reducer from '../reducers/fetch_data';
import thunk from 'redux-thunk';
import { connect } from 'react-redux';
import { fetchData } from '../actions';
class HomeScreen extends React.Component {
static navigationOptions = {
header: null
}
componentDidMount() {
this.props.fetchData()
}
render() {
const { result, isFetching } = this.props.result;
if (isFetching) {
return(
<View style={{flex: 1, flexDirection: 'column', alignItems: 'center', justifyContent: 'center'}}>
<ActivityIndicator size={'large'} />
</View>
)
} else {
return(
<View style={{flex: 1, flexDirection: 'column', alignItems: 'center', justifyContent: 'center'}}>
<Text>{result}</Text>
</View>
)
}
}
}
function mapStateToProps(state) {
return {
result_array: state.result.result_array
}
}
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators({ fetchData }, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'blue'
},
welcome: {
color: '#FFF',
fontSize: 30
}
})
There are a few things you need to fix. It would be better if you upload the codes to github or codesandbox then we can understand you code structure easier.
First of all, return (fetch('url')).then or return fetch(url).then, both of them work as you expect, but you should keep in mind that the redux-thunk doesn't care about you return value, this promise has nothing to do, that's alright, it doesn't need to do anything.
Let's fix your syntax error in fetch_data.js first:
import FETCH_DATA from '../actions'
should be :
import { FETCH_DATA } from '../actions' // I don't know why didn't your build tool report an error here.
According to your purpose, I think you should import the FETCH_SUCCESS here as well.
Then we can talk about your action and reducer flow :
return(fetch('https://api.myjson.com/bins/fz62x'))
.then(res => res.json())
.then(data => {
dispatch({
type: FETCH_DATA,
payload: data
})
}
)
}
and reducer :
switch (action.type) {
case FETCH_DATA: {
return {
...state,
isFetching: true,
result: action.payload.data
}
}
default:
return state
}
You only deal with FETCH_DATA(and I think you probably should use FETCH_SUCCESS here), another action type should be dispatch and parse as well.
I will suggest the flow like this :
Action.js:
export const FETCH_DATA = "fetch_data";
export const FETCH_SUCCESS = "fetch_success";
export function fetchData() {
return dispatch => {
fetch("https://api.myjson.com/bins/fz62x")
.then(res => res.json())
.then(data => {
dispatch({
type: FETCH_SUCCESS,
payload: data
});
});
dispatch({
type: FETCH_DATA
});
};
}
With this you now dispatch FETCH_DATA to tell the application you start to fetch data first, and dispatch FETCH_SUCCESS when fetch succeed.
And the fetch_data.js:
import { FETCH_DATA, FETCH_SUCCESS } from "./actions";
const initialState = {
result: [],
isFetching: false,
error: false
};
export default function fetchReducer(state = initialState, action) {
switch (action.type) {
case FETCH_DATA: {
return {
...state,
isFetching: true,
error: false
};
}
case FETCH_SUCCESS: {
return {
result: action.payload, // The action.payload.data isn't what you want
isFetching: false,
error: false
};
}
default:
return state;
}
}
And now your reducer will respond correctly with FETCH_DATA and FETCH_SUCCESS
And another thing is I don't know why you import thunk and reducer in your
Homescreen.js, it should not be there if you are not going to create you redux store there (I assume that you have a <Provider store={store}>{...your application}</Provider> upon the Homescreen which you should have)
I'm not sure about your problem but hope this help.
First, Your Action.js, you dispatch payload with array:
dispatch({
type: FETCH_DATA,
payload: data
})
that data is array
[
{ createdOn: '', taskList: [{}, {}, ...] },
{ createdOn: '', taskList: [{}, {}, ...] },
...
]
In your fetch_data.js You received payload with array but access .data it will got undefined, you have to change to result: action.payload
return {
...state,
isFetching: true,
result: action.payload.data // <-- undefined
}
but I think you should have type FETCH_DATA to return { isFetching: true } and FETCH_SUCCESS return { isFetching: false, result: action.payload like this
// action.js
fetchData() {
dispatch({
type: FETCH_DATA
})
return fetch('https://api.myjson.com/bins/fz62x')
.then(res => res.json())
.then(data => {
dispatch({
type: FETCH_SUCCESS,
payload: data
})
})
}
// fetch_data.js
switch (action.type) {
case FETCH_DATA: {
return {
...state,
isFetching: true
}
}
case FETCH_SUCCESS: {
return {
...state,
isFetching: false,
result: action.payload
}
}
}
but It looks a bit weird because you got 0 when use get the length of result, it should be undefined unless it return defaultValue of state that result is empty array
You must have 50 reputation to comment
Sorry, but i just seen that your edit 1 has something wrong. iam i right
case FETCH_DATA_SUCCESS: {
return {
...state,
isFetching: false,
result_array: action.payload <-- this is undefined. it should be action.data from your action
}
}

Categories

Resources