React js, Cannot read property "0". When calling an API - javascript

I cannot able to access the data from the fetch function. I want to pass the data from action to reducer. API is called using an fetch function, api is returned in the form of promise. So, API is called separately and data is returned back to the action payload.
import { INDEX_PRESCRIPTION } from '../constant.js';
function fetch_prescription(){
const base_url= "http://192.168.1.22:3000/api/v1/";
const fetch_url = `${base_url}/prescriptions`;
let datas = [];
return fetch(fetch_url, {
method: "GET"
})
.then(response => response.json())
.then(data => {
datas.push(data['prescriptions'])
return datas
})
}
export const indexPrescription = async () => {
const action = {
type: INDEX_PRESCRIPTION,
details: await fetch_prescription()
}
return action;
console.log('action details', action.details)
}
export const getIndexPrescription = (dispatch) => {
dispatch(indexPrescription());
}
On examining the console, we get:
How to access the prescription details. I tried to access it by action.details["0"]["0"] , but results in 'Cannot read property "0" of undefined '. I have gone through many questions and solution related to this problem, but cant able to study what is going wrong with my code.
Update Here is my index.jsx component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { getIndexPrescription } from '../actions/index.js';
class Index extends Component {
constructor(props){
super(props);
this.state = {
prescription: null
}
}
componentWillMount(){
this.props.getIndexPrescription();
}
render(){
return(
<h2>
Prescription Index
</h2>
)
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({ getIndexPrescription }, dispatch)
}
function mapStateToProps(state){
return {
prescription: state
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Index);
And My src/index.js file is
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import {Provider} from 'react-redux';
import reducer from './reducers';
import Index from './components/index.jsx';
const store = createStore(reducer, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<Index />
</Provider>, document.getElementById("root")
)

Your promise is resolved only after you have answer from the server. You need to use additional layer in order to handle async behavior in redux.
For example with redux-thunk, you can make it work like this:
import { INDEX_PRESCRIPTION } from '../constant.js';
function fetch_prescription(){
const base_url= "http://192.168.1.22:3000/api/v1/";
const fetch_url = `${base_url}/prescriptions`;
let datas = [];
return fetch(fetch_url, {
method: "GET"
})
.then(response => response.json())
.then(data => data['prescriptions']);
}
export const indexPrescription = (dispatch) => {
fetch_prescription()
.then(details => {
const action = {
type: INDEX_PRESCRIPTION,
details
}
dispatch(action);
}
}

The part you are missing here is that the function fetch_prescription() is asynchronous. So the data may not be available when you are accessing the data.
You are returning the datas before resolving the asnyc function return datas
You may use it as
import { INDEX_PRESCRIPTION } from '../constant.js';
function fetch_prescription(){
const base_url= "http://192.168.1.22:3000/api/v1/";
const fetch_url = `${base_url}/prescriptions`;
let datas = [];
return fetch(fetch_url, {
method: "GET"
})
.then(response => response.json())
.then(data => {
datas.push(data['prescriptions'])
return datas
})
}
export const indexPrescription = async () => {
const action = {
type: INDEX_PRESCRIPTION,
details: await fetch_prescription()
}
return action;
}
export const getIndexPrescription = (dispatch) => {
dispatch(indexPrescription());
}
And dispatch the above action where ever you want.
Call getIndexPrescription() in componentWillMount

Find the code below to add redux-thunk to your application.
...
import { createStore, applyMiddleware } from 'redux';
import reduxThunk from 'redux-thunk';
...
const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore);
const store = createStoreWithMiddleware(reducers);
<Provider store={store}>
...
</Provider>

Related

Saga Function is not being called

I am using saga.js with Redux in my project and I am trying to call an API but that API is not being called. The generator function is called, but with yield.put() other method is not being called. I am fairly new to Redux Saga and I am stuck here. Any help would be really appreciated.
Saga.js
import { put, takeEvery, all ,fork, takeLatest} from "redux-saga/effects";
import axios from "axios";
function* runOurAction() {
let remoteData;
yield axios.get(url).then((resp) => {
remoteData = resp.data;
});
yield put({ type: "SET_DATA", payload: remoteData });
};
function* getAsyncDataWatcher() {
yield takeLatest("GET_TEAMS", runOurAction);
}
export default function* rootSaga() {
yield fork(getAsyncDataWatcher)
}
getAsyncDataWatcher() is being called but its not calling runOurAction
Reducer.js
const teams=(state=[],action)=>{
switch(action.type) {
case "SAVE_TEAMS":
return { ...state, payload: action.payload };
case "GET_TEAMS":
return { ...state, payload: action.payload };
case "SET_DATA":
return { ...state, payload: action.payload };
default:
return state;
}
}
export default teams;
Actions.js
const getTeams = (payload) => {
return {
type: "GET_TEAMS",
payload:payload
};
};
const saveTeams = (payload) => {
return {
type: "SAVE_TEAMS",
payload:payload
};
};
export { saveTeams, getTeams };
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import rootReducer from "./reducers";
import createSagaMiddleware from "redux-saga";
import { createStore, applyMiddleware ,compose} from "redux";
import { Provider } from "react-redux";
import rootSaga from "./saga";
const sagaMiddleware = createSagaMiddleware();
const enhancers = [window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), applyMiddleware(sagaMiddleware)];
const store = createStore(
rootReducer,
compose(...enhancers)
);
sagaMiddleware.run(rootSaga);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
//
serviceWorker.unregister();
Team.js
import React,{useEffect} from 'react'
import {useSelector,useDispatch} from "react-redux";
import {getTeams} from "../actions";
const Team=()=> {
const data=useSelector(state=>state.teams);
const dispatch=useDispatch();
useEffect(() => {
console.log("Called");
dispatch(getTeams());
}, [dispatch])
console.log("hello",data);
return (
<div>
</div>
)
}
export default Team
The problem is probably caused by axios.get() as the return is a promise. Try the following:
yield axios.get(url).then((resp) => {
return resp.data;
}).then(response => {
remoteData = response;
});
I think you can't simply call axios function directly that way. You have to wrap it in call a saga-effects which only takes a function as its argument not a Promise resolve, so it would look like:
import { call } from 'redux-saga/effects';
// Wrap in a call which takes a function as argument
const remoteData = yield call(() => axios.get(url).then(response => repsonse.data));
// Or you can simply write in shorter way
const { data: remoteData } = yield call(axios.get, url);

Export JSON object to another .js without class declaration react-native

I am developing a mobile app which makes GET calls using fetch api. I am stuck in that I am trying to export json object( fetched from server with fetch method) to another .js file to be used as array, But when I import my function in another.js (below), it returns nothing. I tested my fetch method with console so it works as expected, however I am unable to process data in another.js file. By the way, I have searched a lot and found this post Helpful, but not worked.
Below code is implementation of fetch part and exporting it.(Products.js)
import React, { PureComponent,Component } from "react";
import { connect } from "react-redux";
import { View } from "react-native";
import { productsDataSelector } from "../../Products/ProductsSelectors";
import ProductsList from "../../ProductsList/ProductsList";
import Product from "../../Product/Product";
import { NavigationActions, StackActions } from "react-navigation";
import AnotherComponent from "../../Products/ProductsReducer";
class Products extends PureComponent {
render() {
const { navigation } = this.props;
const { productsData } = this.props;
return (
<View>
<ProductsList list={productsData} isSortable>
{product => <Product product={product} />}
</ProductsList>
</View>
);
}
}
const mapStateToProps = state => ({
productsData: productsDataSelector(state)
});
export const getMoviesFromApiAsync = () =>
fetch('http://localhost:8080/JweSecurityExample/rest/security/retrieveItems')
.then((response) => response.json())
export default connect(
mapStateToProps,
null
) (Products);
Below code is another.js where importing fetch function and processing returning json object without class declaration implemented.
import React, { Component } from "react";
import {getMoviesFromApiAsyncc} from "../screens/Products/Products";
const fakeData = [];
export const someFunc = () => {
fetch('http://localhost:8080/JweSecurityExample/rest/security/retrieveItems')
.then((response) => response.json())
.then((responseJson) => console.log("responsee:"+JSON.stringify(responseJson)))
.then((responseJson) => {fakeData:JSON.stringify(responseJson)})
.catch((error) => {
console.error(error);
});
};
someFunc();
const initialState = {
data:this.fakeData
};
export default (state = initialState,action) => {
return state;
};
Any recommendations ?? Thanx
I don't see where in your code do you call someFunc and one more thing you need to wrap the object that you return from someFunc in braces otherwise it will be treated as the function's body.
export const someFunc = () => {
getMoviesFromApiAsync().then(response => {
fakeData = JSON.stringify(response)
})
};
someFunc();
I suggest that you move getMoviesFromApiAsync to a separate file and call it from your component to get the list of movies.
api.js
export const getMoviesFromApiAsync = () =>
fetch('http://localhost:8080/JweSecurityExample/rest/security/retrieveItems')
.then((response) => response.json());
product.js
import React, { PureComponent,Component } from "react";
import { connect } from "react-redux";
import { View } from "react-native";
import { productsDataSelector } from "../../Products/ProductsSelectors";
import ProductsList from "../../ProductsList/ProductsList";
import Product from "../../Product/Product";
import { NavigationActions, StackActions } from "react-navigation";
import AnotherComponent from "../../Products/ProductsReducer";
// import getMoviesFromApiAsync
import { getMoviesFromApiAsync } from 'PATH_TO_API.JS'
class Products extends Component {
async componentDidMount(){
const list = await getMoviesFromApiAsync();
console.log(list);
}
render() {
const { navigation } = this.props;
const { productsData } = this.props;
return (
<View>
<ProductsList list={productsData} isSortable>
{product => <Product product={product} />}
</ProductsList>
</View>
);
}
}

Redux firing undefined action while using redux thunk

This issue likely stems from a misconfiguration of redux-thunk or a misunderstanding of how to write a thunk. I've tried a lot of different ways, but from what I can tell, this should work. However, I'm still getting a console message that says its firing a redux action of undefined.
Here is my store configuration
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import App from './components/App';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={ store }>
<App />
</Provider>,
document.getElementById('rootElement')
);
Here is my action:
import axios from 'axios';
export const GET_ABOUT_CONTENT_REQUEST = 'GET_ABOUT_CONTENT_REQUEST';
export const GET_ABOUT_CONTENT_FAILED = 'GET_ABOUT_CONTENT_FAILED';
export const GET_ABOUT_CONTENT_OK = 'GET_ABOUT_CONTENT_OK';
export const fetchAboutContent = () => {
const url = `http://localhost:3000/about`;
return (dispatch, getState) => {
if (getState.isInitialized === true){
console.log("desktop init should not be called when already desktop is init")
return Promise.resolve();
}
if (getState.about.isLoading) {
console.log('is loading');
return Promise.resolve();
}
dispatch({ type: GET_ABOUT_CONTENT_REQUEST });
axios.get(url)
.then(res => dispatch({
type: GET_ABOUT_CONTENT_OK,
res
}))
.error(err => dispatch({
type: GET_ABOUT_CONTENT_FAILED,
err
}));
}
}
Here is me firing the action in my component:
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actions from '../../actions/about';
import getAboutContent from '../../reducers';
class AboutMe extends React.Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.getAboutContent();
}
render() {
return <div>{ this.props.content }</div>
}
}
const mapStateToProps = (state) => ({
content: {} || getAboutContent(state)
})
const mapDispatchToProps = (dispatch) =>
bindActionCreators({ getAboutContent }, dispatch)
export default connect(
mapStateToProps, mapDispatchToProps
)(AboutMe);
I've tried quite a few configurations for mapDispatchToProps, i.e. connect(..., { fetchData: getAboutContent })..., and more. Any help is greatly appreciated.
Edit:
Here is the git repo, if that is helpful to anybody: https://github.com/sambigelow44/portfolio-page
Check your reducer name,you export fetchAboutContent, but import getAboutContent.
Code in action file is seems to be incorrect.
getState is a function.
const state = getState();
Change below code.
import axios from 'axios';
export const GET_ABOUT_CONTENT_REQUEST = 'GET_ABOUT_CONTENT_REQUEST';
export const GET_ABOUT_CONTENT_FAILED = 'GET_ABOUT_CONTENT_FAILED';
export const GET_ABOUT_CONTENT_OK = 'GET_ABOUT_CONTENT_OK';
export const fetchAboutContent = () => {
const url = `http://localhost:3000/about`;
return (dispatch, getState) => {
if (getState().isInitialized === true){
console.log("desktop init should not be called when already desktop is init")
return Promise.resolve();
}
if (getState().about.isLoading) {
console.log('is loading');
return Promise.resolve();
}
dispatch({ type: GET_ABOUT_CONTENT_REQUEST });
axios.get(url)
.then(res => dispatch({
type: GET_ABOUT_CONTENT_OK,
res
}))
.error(err => dispatch({
type: GET_ABOUT_CONTENT_FAILED,
err
}));
}
}
Also you need to return promise from axios call, just add return statement.
return axios.get(url)
.then(res => dispatch({
type: GET_ABOUT_CONTENT_OK,
res
}))
.error(err => dispatch({
type: GET_ABOUT_CONTENT_FAILED,
err
}));

Redux thunk fetch return undefined

I'm new with Redux Thunk and I'm having problems with dispatch an action after fetching async call by click on button component.
actions.js
import fetch from 'isomorphic-fetch'
export const getPosts = (json) => {
return {
type: constant.GET_POSTS,
payload: {
data: json
}
}
}
export const loadPosts () => {
return (dispatch) => {
return fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => {
res.json()
}).then(json => {
dispatch(getPosts(json))
})
}
}
button.js
class Button extends React.Component {
clicked(){
console.log(this.props.loadJsonPosts()) // got undefined here
}
render() {
return(
<button onClick={this.clicked.bind(this)}>click</button>
)
}
}
buttonContainer.js
import connect from 'react-redux/lib/components/connect'
import { loadPosts } from '../actions/actions.js'
import Button from '../components/Button'
function mapDispatchToProps(dispatch) {
return {
loadJsonPosts: () => { dispatch(loadPosts()) }
}
}
export default connect(null, mapDispatchToProps)(Button)
reducer.js
import * as constant from '../constants/index'
let initialState = { postList: [] }
const reducer = (state = initialState, action) => {
switch (action.type) {
case constant.GET_POSTS: //here i call my loadPosts action
state = Object.assign({}, { postList: [{ post: action.data }] })
break;
default:
break;
}
return state
}
export default reducer
App.jsx
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Main from './components/Main'
import thunk from 'redux-thunk'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import reducer from './reducers/reducer'
const store = createStore(
reducer,
applyMiddleware(thunk)
)
class App extends Component {
render() {
return(
<Provider store={store}>
<Main />
</Provider>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
)
I can't figure out why i get undefined, maybe I've missed something or I've wrong the approach
You forgot to return res.json() in actions.js for the next then block.
it should be
export const loadPosts () => {
return (dispatch) => {
return fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => {
return res.json();
}).then(json => {
dispatch(getPosts(json))
})
}}
or you can skip the return statement by removing the blocks by writing .then(res => res.json())
I the same issue and found that ensuring the thunk middleware was first in my chain when creating the redux store allowed me to access the promise I was after rather than getting undefined,
store = createStore(
rootReducer,
initialState,
applyMiddleware(thunk, otherMiddleware1, otherMiddleware2)
);
mapDispatchToProps should be like this:
function mapDispatchToProps(dispatch) {
return {
// loadPosts instead of loadPosts()
loadJsonPosts: () => { dispatch(loadPosts) }
} }

Handling api calls in Redux with Axios

Good evening everybody!
I'm a total beginner in React and Redux so please bear with me if this sounds totally stupid. I'm trying to learn how I can perform some API calls in Redux and it's not going all to well. When I console log the request from the action creator the promise value is always "undefined" so I'm not sure if I'm doing this correctly.
My goal is to grab the information from the data inside the payload object and display them inside the component. I've been trying to get this to work for the past days and I'm totally lost.
I'm using Axios for and redux-promise to handle the call.
Any help will be greatly appreciated.
Here's the output from the console.
Action Creator
import axios from 'axios';
export const FETCH_FLIGHT = 'FETCH_FLIGHT';
export function getAllFlights() {
const request = axios.get('http://localhost:3000/flug');
console.log(request);
return {
type: FETCH_FLIGHT,
payload: request
};
}
Reducer
import { FETCH_FLIGHT } from '../actions/index';
export default function(state = [], action) {
switch (action.type) {
case FETCH_FLIGHT:
console.log(action)
return [ action.payload.data, ...state ]
}
return state;
}
Component
import React from 'react';
import { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { getAllFlights } from '../actions/index';
import Destinations from './Destinations';
class App extends Component {
componentWillMount(){
this.props.selectFlight();
}
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<div>
</div>
);
}
function mapStateToProps(state) {
return {
dest: state.icelandair
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ selectFlight: getAllFlights }, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
axios is the promise so you need to use then to get your result. You should request your api in a separate file and call your action when the result comes back.
//WebAPIUtil.js
axios.get('http://localhost:3000/flug')
.then(function(result){
YourAction.getAllFlights(result)
});
In your action file will be like this :
export function getAllFlights(request) {
console.log(request);
return {
type: FETCH_FLIGHT,
payload: request
};
}
You can do this with thunk. https://github.com/gaearon/redux-thunk
You can dispatch an action in your then and it will update state when it gets a response from the axios call.
export function someFunction() {
return(dispatch) => {
axios.get(URL)
.then((response) => {dispatch(YourAction(response));})
.catch((response) => {return Promise.reject(response);});
};
}
I also think the best way to do this is by redux-axios-middleware. The setup can be a bit tricky as your store should be configured in a similar way:
import { createStore, applyMiddleware } from 'redux';
import axiosMiddleware from 'redux-axios-middleware';
import axios from 'axios';
import rootReducer from '../reducers';
const configureStore = () => {
return createStore(
rootReducer,
applyMiddleware(axiosMiddleware(axios))
);
}
const store = configureStore();
And your action creators now look like this:
import './axios' // that's your axios.js file, not the library
export const FETCH_FLIGHT = 'FETCH_FLIGHT';
export const getAllFlights = () => {
return {
type: FETCH_FLIGHT,
payload: {
request: {
method: 'post', // or get
url:'http://localhost:3000/flug'
}
}
}
}
The best way to solve this is by adding redux middlewares http://redux.js.org/docs/advanced/Middleware.html for handling all api requests.
https://github.com/svrcekmichal/redux-axios-middleware is a plug and play middleware you can make use of.
I took care of this task like so:
import axios from 'axios';
export const receiveTreeData = data => ({
type: 'RECEIVE_TREE_DATA', data,
})
export const treeRequestFailed = (err) => ({
type: 'TREE_DATA_REQUEST_FAILED', err,
})
export const fetchTreeData = () => {
return dispatch => {
axios.get(config.endpoint + 'tree')
.then(res => dispatch(receiveTreeData(res.data)))
.catch(err => dispatch(treeRequestFailed(err)))
}
}

Categories