Unable to display information after fetching data from useSelector - reactjs

I'm trying to access data from my API using Redux but when redux tool kit is showing me its an empty array. The api I've populated using postman and the post method seem to work perfectly fine, but attempting to use the get method to access that data it shows an empty array. My DB has the data though. My Data is an array of Object i.e. [ {...} , {...} , {...} ]
API
import axios from "axios";
const url = "http://localhost:5000/info"
export const fetchInfo = () => axios.get(url);
export const createInfo = (newInfo) => axios.post(url, newInfo);
ACTIONS
import * as api from "../api/index.js";
//constants
import { FETCH_ALL, CREATE } from "../constants/actiontypes";
export const getInfo = () => async (dispatch) => {
try {
const { data } = await api.fetchInfo();
console.log(data);
dispatch({ type: FETCH_ALL, payload: data });
} catch (error) {
console.log(error);
}
};
export const createInfo = (info) => async (dispatch) => {
try {
const { data } = await api.createInfo(info);
dispatch({ type: CREATE, payload: data });
} catch (error) {
console.log(error);
}
};
REDUCER
import { FETCH_ALL, CREATE } from "../constants/actiontypes";
export default (infos = [], action) => {
switch (action.type) {
case FETCH_ALL:
return action.payload;
case CREATE:
return [...infos, action.payload];
default:
return infos;
}
};
COMBINE REDUCERS
import {combineReducers} from "redux";
import infos from "./info"
export default combineReducers({infos})
Component I'm trying to to display it in
import React from "react";
//redux
import { useSelector } from "react-redux";
//component
import MovieDetail from "./MovieDetail"
const MovieTitles = () => {
const infos = useSelector((state) => state.infos);
console.log(infos) // shows me empty array
return (
<div>
{infos.map((i) => (
<MovieDetail info={i} />
))}
</div>
);
};
export default MovieTitles;
Is there something else I'm missing which allows to me to access the data?
thanks

Related

Calling API in redux

//Store
import { configureStore } from "#reduxjs/toolkit";
import { currencyListSlice } from "./Reducers/CurrencyListReducer";
export const store = configureStore({
reducer: {
currencyList: currencyListSlice.reducer,
}
}
)
export default store
//CurrencyListReducer
import { createSlice } from "#reduxjs/toolkit"
export const loadCurrencyList = () => {
return async (dispatch, getState) => {
const data = await fetch(API-Key)
const payload = await data.json()
dispatch({
type: 'currencyList/setCurrencyList',
payload: payload
})
}
}
const options = {
name: 'currencyList',
initialState: [],
reducers: {
setCurrencyList(state, action) {
return action.payload
}
}
}
export const currencyListSlice = createSlice(options)
//CurrencyList Component
import React, { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { currencyListSlice } from '../../Reducers/CurrencyListReducer'
const selectCurrencyList = state => state.CurrencyList
export const CurrencyList = () => {
const dispatch = useDispatch()
const currencyList = useSelector(selectCurrencyList)
const { loadCurrencyList } = currencyListSlice.actions
useEffect(() => {
dispatch(loadCurrencyList())
}, [dispatch, loadCurrencyList])
console.log(currencyList)
return (
<div>
/*Some elements here*/
</div>
)
}
I'm working with redux for the first time and having some real problem in calling API and storing data in store. The problem is I'm not getting anything from API but the console.log(currencyList) just gives me undefined. I tried calling API directly in reducer but that too didn't work out. I'm a newbie to redux and calling the API in redux is being a difficult task for me. Forgive any silly mistake(if present).
try reading this: createAsyncThunk

Data is not passing to component in React Redux

I am working in a React Redux project.
My Reducer is like below
statusReducer.js
import { FETCH_STATUS } from "../firebase/types";
export default (state = false, action) => {
switch (action.type) {
case FETCH_STATUS:
return action.payload || null;
default:
return state;
}
};
My dispatcher is like below
Status.js
import firebase from 'firebase/app';
import { todosRef, authRef, provider } from './firebase';
import { FETCH_STATUS, FETCH_ONESTATUS } from './types.js';
const databaseRef = firebase.firestore();
export const fetchStatus = uid => async dispatch => {
var data = [];
databaseRef.collection('status').doc(uid).collection('status').get().then((snapshot) => {
snapshot.forEach((doc) => {
var snap = doc.data();
snap.key = doc.id;
data.push(snap);
console.log(data); // I am getting values here
});
dispatch({
type: FETCH_STATUS,
payload: data,
});
});
};
My Component is like below
import React from "react";
import * as actions from './firebase/Status';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom'
class Status extends React.Component {
componentWillMount() {
const { auth } = this.props;
this.props.fetchStatus(auth.uid);
};
renderStatus() {
const { status } = this.props;
const data = status ? Object.values(status) : [];
console.log(status) // I am not getting values here
}
render() {
return (
//more HTML code
);
}
}
const mapStateToProps = ({ auth, status }) => {
return {
auth,
status,
}
}
export default connect(
mapStateToProps,
actions
)(Status);
There is some problem with your Status.js file. You're fetching data from firebase and try to dispatch it. But the problem is your program dispatch before getting data form firebase.
So you can use JS Promise for that. Maybe this will help!!!
...
const request = snapshot.forEach((doc) => {
return new Promise((resolve) => {
var snap = doc.data();
snap.key = doc.id;
console.log(data); // I am getting values here
});
});
Promise.all(request).then(() => {
dispatch({
type: FETCH_STATUS,
payload: data,
});
});
...

Should I add async code in container component?

I'm making my first React-Redux project.
I wanna get data from getListAPI.
I checked console.log(data) in [GET_LIST_SUCCESS], and there was what I wanted.
But console.log(temp) in container, I expect 'data', it was just action object(only type exists).
How can I get the 'data'?
// container
import React from 'react';
import { useDispatch } from 'react-redux';
import Home from 'presentations/Home';
import * as homeActions from 'modules/home';
const HomeContainer = () => {
const dispatch = useDispatch();
const temp = dispatch(homeActions.getList());
console.log(temp);
return (
<Home />
);
}
export default HomeContainer;
// Redux module
import axios from 'axios';
import { call, put, takeEvery } from 'redux-saga/effects';
import { createAction, handleActions } from 'redux-actions';
function getListAPI() {
return axios.get('http://localhost:8000/');
}
const GET_LIST = 'home/GET_LIST';
const GET_LIST_SUCCESS = 'home/GET_LIST_SUCCESS';
const GET_LIST_FAILURE = 'home/GET_LIST_FAILURE';
export const getList = createAction(GET_LIST);
function* getListSaga() {
try {
const response = yield call(getListAPI);
yield put({ type: GET_LIST_SUCCESS, payload: response });
} catch (e) {
yield put({ type: GET_LIST_FAILURE, payload: e });
}
}
const initialState = {
data: {
id: '',
title: '',
created_at: '',
updated_at: '',
content: '',
view: '',
}
};
export function* homeSaga() {
yield takeEvery('home/GET_LIST', getListSaga);
}
export default handleActions(
{
[GET_LIST_SUCCESS]: (state, action) => {
const data = action.payload.data;
console.log(data);
return {
data
};
}
}, initialState
);
Maybe I need like async/await or Promise.then() or useCallback, etc in container?
Because I thought Redux-Saga handles async, but container isn't in Redux-Saga area.
So shouldn't I inject the container with async processing?
I wrote some code for test.
Expecting to receive other data in a few seconds.
// container
// const temp = dispatch(homeActions.getList());
let temp = dispatch(homeActions.getList());
let timer = setInterval(() => console.log(temp), 1000);
setTimeout(() => { clearInterval(timer); alert('stop');}, 50000);
Nothing changed.
It's just log action object(only type exists).
What am I missing?
dispatch() returns the action dispatched to the store (that's why the console.log(temp) shows the action itself).
You need to create a selector to fetch the data from the store and use the useSelector() hook:
// container
import React from 'react';
import { useDispatch } from 'react-redux';
import Home from 'presentations/Home';
import * as homeActions from 'modules/home';
const selectData = (state) => state.data
const HomeContainer = () => {
const dispatch = useDispatch();
const temp = useSelector(selectData)
dispatch(homeActions.getList());
// Do something with temp
return (
<Home />
);
}
export default HomeContainer;

Calling array getting error: undefined is not an object

There is some issue with my reducer. I can pull from the database array on command+S but when I do a hard refresh it cannot find the data.
And I get error: undefined is not an object (evaluating 'quizFire[0]')
I can load the array quizFire, I just can't call any of its elements. I've tried a number of options but I am not sure if I'm not storing or calling the database correctly.
import React, {useEffect} from 'react';
import { View, Text, } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import * as paidRoundActionFire from '../store/action/paidRoundAction';
const ThirdRound = props =>{
const dispatch = useDispatch();
useEffect(() => {
dispatch(paidRoundActionFire.fetchProducts());
}, [dispatch]);
const quizFire = useSelector(state => state.paidQuiz.questionsFire);
return(
<View>
<Text>
{quizFire[0].answer1}
</Text>
</View>
)
}
export default ThirdRound;
//Store Reducer
import { Dimensions } from 'react-native';
import { SET_QUESTIONS } from '../action/paidRoundAction';
var {height, width} = Dimensions.get('window');
//const incrementToMoveForward = (width+30)/20;
const initialState = {
//totalCount: questions.length,
questionsFire: [],
};
export default (state = initialState, action) => {
//state.activeQuestionIndex + 1;
switch (action.type) {
case SET_QUESTIONS: {
return {
questionsFire: action.questionsFireRecall,
}
}
default:
return {state};
}
};
//Store Action
import Questions from "../../models/questions";
export const SET_QUESTIONS = 'SET_QUESTIONS';
export const fetchProducts = () => {
return async dispatch => {
try {
//any async code you want
const response = await fetch(
'https://my-quiz-questions.firebaseio.com/questions.json',)
const resData = await response.json();
console.log("async/await based");
const loadedQuestions = [];
for (const key in resData) {
loadedQuestions.push(
new Questions(
key,
resData[key].answer1,
resData[key].answer2,
resData[key].answer3,
resData[key].correctAnswer,
resData[key].explanation,
resData[key].question
)
)
}
dispatch({ type: SET_QUESTIONS, questionsFireRecall: loadedQuestions });
}
catch (err) {
// send to custom analytics server
throw err;
}
};
};
At the beginning you have quizFire set as empty array []. But you want to render some item from this empty array and this is undefined. Try to render it like quizFire.length && quizFire[0].something
Why are you putting dispatch as useEffect deps? Because litter is telling you to do that or you did it in purpose?

Why do I get a 'Actions must be plain objects' error?

I am just learning react-redux and trying to fire a thunk, this is the thunk:
const getRepos = dispatch => {
try {
const url = `https://api.github.com/users/reduxjs/repos?sort=updated`;
fetch(url)
.then(response => response.json())
.then(json => {
console.log("thunk: getrepos data=", json);
});
} catch (error) {
console.error(error);
}
};
I hooked up my component to the store:
const bla = dispatch =>
bindActionCreators(
{
geklikt,
getRepos
},
dispatch
);
const Container = connect(
null,
bla
)(Dumb);
When I trigger the getRepos thunk I get:
Actions must be plain objects. Use custom middleware for async
actions.
What could be the issue? I included the middleware? link to code
sandbox
Please refactor your application structure, it's all in one file and extremely hard to read.
Things to consider:
Use a switch statement in your reducers
Separate components from containers: https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
Make sure to set initial reducer state: state={}, state=[] ...etc.
Simplify the action to either use .then().catch() or use async/await within a try/catch block.
In the meantime, here's a working version: https://codesandbox.io/s/oxwm5m1po5
actions/index.js
import { GEKLIKT } from "../types";
export const getRepos = () => dispatch =>
fetch(`https://api.github.com/users/reduxjs/repos?sort=updated`)
.then(res => res.json())
.then(data => dispatch({ type: GEKLIKT, payload: data }))
.catch(err => console.error(err.toString()));
/*
export const getRepos = () => async dispatch => {
try {
const res = await fetch(`https://api.github.com/users/reduxjs/repos?sort=updated`)
const data = await res.json();
dispatch({ type: GEKLIKT, payload: data }))
} catch (err) { console.error(err.toString())}
}
*/
components/App.js
import React from "react";
import Dumb from "../containers/Dumb";
export default () => (
<div className="App">
<Dumb />
</div>
);
containers/Dumb.js
import React from "react";
import { connect } from "react-redux";
import { getRepos } from "../actions";
let Dumb = ({ data, getRepos }) => (
<div>
hi there from Dumb
<button onClick={getRepos}>hier</button>
<pre>
<code>{JSON.stringify(data, null, 4)}</code>
</pre>
</div>
);
export default connect(
state => ({ data: state.data }),
{ getRepos }
)(Dumb);
reducers/index.js
import { combineReducers } from "redux";
import { GEKLIKT } from "../types";
const klikReducer = (state = {}, { payload, type }) => {
switch (type) {
case GEKLIKT:
return { ...state, data: payload };
default:
return state;
}
};
export default combineReducers({
data: klikReducer
});
root/index.js
import React from "react";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunk from "redux-thunk";
import rootReducer from "../reducers";
import App from "../components/App";
const store = createStore(rootReducer, applyMiddleware(thunk));
export default () => (
<Provider store={store}>
<App />
</Provider>
);
types/index.js
export const GEKLIKT = "GEKILKT";
index.js
import React from "react";
import { render } from "react-dom";
import App from "./root";
import "./index.css";
render(<App />, document.getElementById("root"));
You returned the promise in action. A promise is not a plain object and so the returned action would not be a plain object and hence the error.
Since you're using the thunk middleware your actions can be functions and here's how you'd do it.
const GET_REPOS_REQUEST = "GET_REPOS_REQUEST";
const GET_REPOS_SUCCESS = "GET_REPOS_SUCCESS";
const GET_REPOS_ERROR = "GET_REPOS_ERROR";
export function getRepos() {
return function action(dispatch) {
dispatch({type: GET_REPOS})
const url = `https://api.github.com/users/reduxjs/repos?sort=updated`;
const request = fetch(url);
return request.then(response => response.json())
.then(json => {
console.log("thunk: getrepos data=", json);
dispatch({type: GET_REPOS_SUCCESS, json});
})
.then(err => {
dispatch({type: GET_REPOS_ERROR, err});
console.log(“error”, err);
});
};
}
Arrow function way:
export getRepos = () =>{
return action = dispatch => {
dispatch({type: GET_REPOS})
const url = `https://api.github.com/users/reduxjs/repos?sort=updated`;
const request = fetch(url);
return request.then(response => response.json())
.then(json => {
console.log("thunk: getrepos data=", json);
dispatch({type: GET_REPOS_SUCCESS, json});
})
.then(err => {
console.log(“error”, err);
dispatch({type: GET_REPOS_ERROR, err});
});
};}

Resources