I want to call a rest api with react-redux but my fetch doesn't called at all.
My actions:
restActions.js file:
export const customerbyidGetAction = (data) => ({ type: types.customerbyidGetAction, data });
My reducer:
restReducer.js file:
import { Map, fromJS } from 'immutable';
import * as types from '../constants/restConstants';
const initialState = {};
const initialImmutableState = fromJS(initialState);
export default function reducer(state = initialImmutableState, action) {
switch(action.type) {
case types.customerbyidGetAction:
return {
...state,
data: action.payload
}
break;
default:
// the dispatched action is not in this reducer, return the state unchanged
return state;
}
}
restApi.js file:
import {customerbyidGetAction} from '../../redux/actions/restActions'
const URL = "https://***"
export default function customerbyidGet() {
return dispatch => {
console.log("not called")
dispatch(customerbyidGetAction());
fetch(URL + 'customer/byid/Get',{
method:'POST',
headers:{
'Accept': 'application/json',
'Content-Type': 'application/json',
'token':1234
},
body: JSON.stringify({'customerId': 1})
})
.then(res => {
const r = res.json();
return r;
})
.then(res => {
if(res.error) {
throw(res.error);
}
dispatch(customerbyidGetAction(res));
return res;
})
.catch(error => {
dispatch(customerbyidGetAction(error));
})
}
}
export default apis;
inside my Component:
import React from 'react';
import { Helmet } from 'react-helmet';
import Grid from '#material-ui/core/Grid';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { withStyles } from '#material-ui/core/styles';
import PropTypes from 'prop-types';
import {
Help, InsertDriveFile,
MonetizationOn,
Person,
PersonPin,
RemoveRedEye
} from '#material-ui/icons';
import { Link } from 'react-router-dom';
import CounterWidget from '../../../components/Counter/CounterWidget';
import colorfull from '../../../api/palette/colorfull';
import styles from '../../../components/Widget/widget-jss';
import PapperBlock from '../../../components/PapperBlock/PapperBlock';
import {customerbyidGetAction} from '../../../redux/actions/restActions';
class Panelclass extends React.Component {
componentDidMount(){
const {customerbyidGet} = this.props;
customerbyidGet()
}
render() {
const { classes } = this.props;
return (
<div>
Hi
</div>
);
}
}
Panelclass.propTypes = {
classes: PropTypes.object.isRequired,
customerbyidGet: PropTypes.func.isRequired,
};
// const mapStateToProps = state => ({
// customers: customerbyidGet(state),
// })
const mapDispatchToProps = dispatch => bindActionCreators({
customerbyidGet: customerbyidGetAction
}, dispatch)
const Panel = connect(
//mapStateToProps,
null,
mapDispatchToProps
)(Panelclass);
export default withStyles(styles)(Panel);
Your reducer expects payload property in action:
function reducer(state = initialImmutableState, action) {
switch (action.type) {
case types.customerbyidGetAction:
return {
...state,
data: action.payload, // Here
};
}
}
So, you need to have that property in action creator:
const customerbyidGetAction = (data) => ({
type: types.customerbyidGetAction,
payload: data, // Here
});
Also, you need to fix the correct action import in component:
import { customerbyidGet } from "../path-to/restApi";
const mapDispatchToProps = (dispatch) =>
bindActionCreators(
{
customerbyidGet,
},
dispatch
);
PS: Today, you should use the Redux Toolkit library which reduces lots of boilerplate code.
First of all define your rest api function like below
export default function customerbyidGet(dispatch) {
return () => {
console.log("not called")
dispatch(customerbyidGetAction());
fetch(URL + 'customer/byid/Get',{
method:'POST',
headers:{
'Accept': 'application/json',
'Content-Type': 'application/json',
'token':1234
},
body: JSON.stringify({'customerId': 1})
})
.then(res => {
const r = res.json();
return r;
})
.then(res => {
if(res.error) {
throw(res.error);
}
dispatch(customerbyidGetAction(res));
return res;
})
.catch(error => {
dispatch(customerbyidGetAction(error));
})
}
}
Then pass it to your mapDispatchToProps function
const mapDispatchToProps = dispatch => bindActionCreators({
customerbyidGetData: customerbyidGet(dispatch)
}, dispatch)
And finally`invoke it in mapDispatchToProps
componentDidMount(){
const {customerbyidGetData} = this.props;
customerbyidGetData()
}
The other two answers are pointing out good points, but missing something very crucial: That ; there is a typo.
const {customerbyidGet} = this.props;
customerbyidGet()
should be
const {customerbyidGet} = this.props.customerbyidGet()
Related
I have an actions file called menu that uses axios to get JSON data from a backend API. I then have reducers which are called in a MainMenu.js file which gets the data. All of the data is shown in the Redux devtools. However I am only able to access the data I have in intitialState. There should be an array of three items but it only shows the first one in intitialState. The data from the reducers does not show up when I try to console.log it. So obviously I would not be able to render it to the DOM.
actions/menu.js
import { ADD_PRODUCT, GET_PRODUCTS, GET_PRODUCT } from './types';
import axios from 'axios';
export const getProducts = () => (dispatch) => {
axios
.get('http://localhost:8080/menu')
.then((response) => {
// console.log(response);
const data = response.data;
dispatch({
type: GET_PRODUCTS,
payload: { data },
});
})
.catch((err) => {
console.log(err);
});
};
export const getProduct = (product) => (dispatch) => {
axios
.get(`http://localhost:8080/menu/${product}`)
.then((response) => {
// console.log(response);
const data = response.data;
dispatch({
type: GET_PRODUCT,
payload: { data },
});
})
.catch((err) => {
console.log(err);
});
};
reducers/index.js
import { combineReducers } from 'redux';
import menu from './menu';
export default combineReducers({ menu });
reducers/menu.js
import { GET_PRODUCTS, GET_PRODUCT } from '../actions/types';
const intitialState = [
{
test: 'Test',
},
];
export default function (state = intitialState, action) {
const { type, payload } = action;
switch (type) {
case GET_PRODUCTS:
return [...state, payload];
case GET_PRODUCT:
return [...state, payload];
default:
return [...state];
}
}
components/MainMenu.js
import React, { Fragment, useEffect } from 'react';
import { connect } from 'react-redux';
import { getProducts, getProduct } from '../actions/menu';
const MainMenu = ({ getProducts, getProduct, menu }) => {
useEffect(() => {
getProducts();
getProduct('b4bc2e28-21a3-47ea-ba3b-6bad40b35504');
console.log(menu);
}, []);
return <Fragment></Fragment>;
};
const mapStateToProps = (state) => ({
menu: state,
});
export default connect(mapStateToProps, { getProducts, getProduct })(MainMenu);
I created a working solution using the ternary operator. First a check is made to see if all of the elements in the array have loaded. If they have not all loaded then a data not loaded div is displayed. Otherwise the data is rendered to the DOM.
import React, { Fragment, useEffect } from 'react';
import { connect } from 'react-redux';
import { getProducts, getProduct } from '../actions/menu';
const MainMenu = ({ getProducts, getProduct, menu }) => {
useEffect(() => {
getProducts();
getProduct('b4bc2e28-21a3-47ea-ba3b-6bad40b35504');
}, []);
if (menu.length <= 2) {
console.log('Data Not Loaded');
} else {
console.log('Data', menu[2].data.name);
}
return (
<Fragment>
<div>{menu.length <= 2 ? <div>Data not loaded</div> : <div>{menu[2].data.name}</div>}</div>
<div>
{menu.length <= 2 ? (
<div>Data not loaded</div>
) : (
<div>
{menu[1].data.map((food) => (
<div>{food.name}</div>
))}
</div>
)}
</div>
</Fragment>
);
};
const mapStateToProps = (state) => ({
menu: state.menu,
});
export default connect(mapStateToProps, { getProducts, getProduct })(MainMenu);
Getting a weird error where 'map' is undefined. I'm not sure if my functions are firing at the wrong time and that's resulting in no data being received.
I'm adding Redux into my simple little application that just pulls data from an API and displays it. It's a list of a bunch of Heroes. Like I said before, I think that the error is coming from different times in the ansyc API call and when Redux is firing. But then again I'm a novice so any help is much appreciated.
import React, {useEffect} from 'react'
import { connect } from 'react-redux'
import { fetchHeroes } from '../actions/heroesActions'
import { Hero } from '../components/Hero'
const HeroesPage = ({ dispatch, loading, heroes, hasErrors }) => {
useEffect(() => {
dispatch(fetchHeroes())
}, [dispatch])
const renderHeroes = () => {
if (loading) return <p>Loading posts...</p>
if (hasErrors) return <p>Unable to display posts.</p>
return heroes.map(hero => <Hero key={hero.id} hero={hero} />)
}
return (
<section>
<h1>Heroes</h1>
{renderHeroes()}
</section>
)
}
// Map Redux state to React component props
const mapStateToProps = state => ({
loading: state.heroes.loading,
heroes: state.heroes.heroes,
hasErrors: state.heroes.hasErrors,
})
export default connect(mapStateToProps)(HeroesPage)
export const GET_HEROES = 'GET HEROES'
export const GET_HEROES_SUCCESS = 'GET_HEROES_SUCCESS'
export const GET_HEROES_FAILURE = 'GET_HEROES_FAILURE'
export const getHeroes = () => ({
type: GET_HEROES,
})
export const getHeroesSuccess = heroes => ({
type: GET_HEROES_SUCCESS,
payload: heroes,
})
export const getHeroesFailure = () => ({
type: GET_HEROES_FAILURE,
})
export function fetchHeroes() {
return async dispatch => {
dispatch(getHeroes())
try {
const response = await fetch('https://api.opendota.com/api/heroStats')
console.log(response)
const data = await response.json()
dispatch(getHeroesSuccess(data))
} catch (error) {
dispatch(getHeroesFailure())
}
}
}
index.js where I created the store
// External imports
import React from 'react'
import { render } from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
// Local imports
import App from './App'
import rootReducer from './reducers'
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)))
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
import React, {useEffect} from 'react'
import { useSelector } from 'react-redux'
import { fetchHeroes } from '../actions/heroesActions'
import { Hero } from '../components/Hero'
const HeroesPage = () => {
const data = useSelector(state => state.heroes);
useEffect(() => {
fetchHeroes();
}, [])
const renderHeroes = () => {
if (data.loading) return <p>Loading posts...</p>
if (data.hasErrors) return <p>Unable to display posts.</p>
return data.heroes.map(hero => <Hero key={hero.id} hero={hero} />)
}
return (
<section>
<h1>Heroes</h1>
{renderHeroes()}
</section>
)
}
export default HeroesPage
action file
// import store from your createStore file and access dispatch from it
dispatch = store.dispatch
export const GET_HEROES = 'GET HEROES'
export const GET_HEROES_SUCCESS = 'GET_HEROES_SUCCESS'
export const GET_HEROES_FAILURE = 'GET_HEROES_FAILURE'
export const getHeroes = () => ({
type: GET_HEROES,
})
export const getHeroesSuccess = heroes => ({
type: GET_HEROES_SUCCESS,
payload: heroes,
})
export const getHeroesFailure = () => ({
type: GET_HEROES_FAILURE,
})
export const fetchHeroes = () => {
dispatch(getHeroes())
try {
const response = await fetch('https://api.opendota.com/api/heroStats')
console.log(response)
const data = await response.json()
dispatch(getHeroesSuccess(data))
} catch (error) {
dispatch(getHeroesFailure())
}
}
reducer file
import * as actions from '../actions/heroesActions'
export const initialState = {
heroes: [],
loading: false,
hasErrors: false,
}
export default function heroesReducer(state = initialState, action) {
switch (action.type) {
case actions.GET_HEROES:
return { ...state, loading: true }
case actions.GET_HEROES_SUCCESS:
return { heroes: action.payload, loading: false, hasErrors: false }
case actions.GET_HEROES_FAILURE:
return { ...state, loading: false, hasErrors: true }
default:
return state
}
}
I am trying to send a GET request using axios and React Saga.
The request fired twice
This is my component file where I call the getBusinessHoursList action:
import { getBusinessHoursList } from "../../../../redux/actions";
...
...
componentDidMount() {
this.props.getBusinessHoursList(this.state.id);
}
...
...
const mapStateToProps = ({ settings }) => {
return {
settings
};
};
export default connect(
mapStateToProps,
{
getBusinessHoursList,
}
)(injectIntl(BusinessHours));
This is my service file where I use axios to get my business hours list:
setting-service.js:
import axios from '../util/api';
import { configureStore } from '../redux/store';
export const settingsService =
{
getBusinessHours,
};
function getBusinessHours() {
const store = configureStore({});
const user = JSON.parse(store.getState().authUser.user)
return axios.get("business/" + user.business.id + "/businesshours")
.then(response => {
return response.data.data
}).catch(error => {
return error.response.data
})
}
This is actions file where I define the actions
actions.js:
import {
CHANGE_LOCALE,
SETTING_GET_BUSINESS_HOURS_FAIL,
SETTING_GET_BUSINESS_HOURS_SUCCESS,
SETTING_GET_BUSINESS_HOURS,
} from '../actions';
export const getBusinessHoursList = (data) => ({
type: SETTING_GET_BUSINESS_HOURS,
payload: data
});
export const getBusinessHoursSuccess = (items) => ({
type: SETTING_GET_BUSINESS_HOURS_SUCCESS,
payload: items
});
export const getBusinessHoursFail = (error) => ({
type: SETTING_GET_BUSINESS_HOURS_FAIL,
payload: error
});
reducer.js
import {
CHANGE_LOCALE,
SETTING_GET_BUSINESS_HOURS,
SETTING_GET_BUSINESS_HOURS_FAIL,
SETTING_GET_BUSINESS_HOURS_SUCCESS
} from '../actions';
const INIT_STATE = {
errors: '',
loadingBH: false,
businessHoursItems: null
};
export default (state = INIT_STATE, action) => {
switch (action.type) {
case SETTING_GET_BUSINESS_HOURS:
return { ...state, loadingBH: false };
case SETTING_GET_BUSINESS_HOURS_SUCCESS:
return { ...state, loadingBH: true, businessHoursItems: action.payload};
case SETTING_GET_BUSINESS_HOURS_FAIL:
return { ...state, loadingBH: true, errors: action.payload };
default: return { ...state };
}
}
saga.js:
import { all, call, fork, put, takeEvery, takeLatest, take } from "redux-saga/effects";
import { getDateWithFormat } from "../../helpers/Utils";
import { settingsService } from '../../services/settings-service'
import {
SETTING_GET_BUSINESS_HOURS,
SETTING_UPDATE_BUSINESS_HOURS,
} from "../actions";
import axios from "../../util/api";
import { NotificationManager } from "../../components/common/react-notifications";
import {
getBusinessHoursSuccess,
getBusinessHoursFail,
} from "./actions";
const getServiceListRequest = async () =>
await settingsService.getBusinessHours()
.then(authUser => authUser)
.catch(error => error);
function* getBusinessHours() {
console.log('test')
try {
const response = yield call(getServiceListRequest);
yield put(getBusinessHoursSuccess(response));
} catch (error) {
yield put(getBusinessHoursFail(error));
}
}
export function* watchGetBusinessHours() {
yield takeLatest(SETTING_GET_BUSINESS_HOURS, getBusinessHours);
}
export default function* rootSaga() {
yield all([
fork(watchGetBusinessHours),
]);
}
Global sagas file : sagas.js:
import { all } from 'redux-saga/effects';
import authSagas from './auth/saga';
import settingsSagas from './settings/saga';
export default function* rootSaga(getState) {
yield all([
authSagas(),
settingsSagas(),
]);
}
The request fired successfully and I get Business hours list but the request fired twice
I tried to use takeLatest in place of takeEvery
This is the network tab
I am trying to fetch data from an api, but I am getting an error: TypeError: this.props.getPeople is not a function, while everything looks good through the code, as below:
people-component.js
import React, { Component } from 'react';
import './people-component-styles.css';
import { Container, Row, Col, Card } from 'react-bootstrap';
import { connect } from 'react-redux';
import { getPeople } from '../../actions/index'
import 'react-lazy-load-image-component/src/effects/blur.css';
import 'animate.css/animate.min.css';
export class People extends Component {
componentDidMount() {
// console.log(this.props);
this.props.getPeople();
}
render() {
// console.log(this.props);
return (
<Row className='main'>
hello!
</Row>
);
}
}
const mapStateToProps = state => ({
people: state.people
})
const mapDispatchToProps = dispatch => ({
getPeople: () => dispatch(getPeople())
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(People);
actions/index.js
export const getPeople = () => {
console.log("yes");
return async dispatch => {
const response = await fetch('https://dma.com.eg/api.php?action=getPeople', {
method: 'GET'
})
const json = await response.json();
dispatch({ type: "GET_PEOPLE", payload: json });
}
}
reducers/index.js
const INITIAL_STATE = {
people: []
}
const rootReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case "GET_PEOPLE":
return ({
...state,
people: state.people.concat(action.payload)
})
default:
return state;
}
};
export default rootReducer
I was importing People component as name while it's exported as default, thanks to #Brian Thompson .. it's fixed.
I can't set the state with mapDispatchToProps and pass the payload
Imports:
import React, { Component } from 'react';
import classes from './HoursWatched.mod ule.css';
import axios from 'axios';
import { connect } from 'react-redux';
import * as actionType from '../../../store/actions';
This is how I try to trigger the action:
componentDidMount() {
this.getHoursWatched()
}
getHoursWatched() {
axios.get(API_URL, header)
.then((res) => {
console.log(res)
this.props.hoursWatched(res)
})
.catch((err) => {
console.log("Error", err)
})
}
The mapDiscpatchToProps:
const mapDispatchToProps = dispatch => {
return {
hoursWatched : (res) => dispatch({ type: actionType.HOURS_WATCHED, payload: res })
}
}
Exports:
export default connect(mapStateToProps, mapDispatchToProps)(HoursWatched);
In the reducer:
const reducer = (state = initialState, action, payload) => {
switch (action.type) {
case actionTypes.HOURS_WATCHED:
const newHoursWatched = {};
console.log(payload) //This is undefined
return {
...state,
hoursWatched: newHoursWatched
};
Why I can't pass the payload? It says it's undefined
You are trying to refer to a wrong argument in the reducer. In order to access the payload that you are passing from the API response you need to use action.payload in the reducer instead of payload.