not a function error in redux - javascript

I have made two api calls in actions. The code for reducer is something like this-
import {combineReducers} from 'redux';
import {GET_API_DATA, GET_QUERY_LIST} from '../actions/index.js';
function getApplicationData(state = [],action){
switch(action.type){
case GET_API_DATA:
return[
...state,
{
resultMeta:action.response,
}
]
default:
return state
case GET_QUERY_LIST:
return[
...state,
{
resultMeta:action.response
}
]
}
}
const data = combineReducers({
getApplicationData
})
export default data;
And in action, I am making a call to the APIs like this-
import * as axios from 'axios';
import {Constants} from '../constants.js';
export const GET_API_DATA = 'GET_API_DATA';
export const getApi = ()=>{
// const res=await axios.get('Constants.URLConst+"/UserProfile"',{headers:{Constants.headers}});
// dispatch({type:GET_API_DATA,payload:res.data});
return(d)=>{
axios({
method:'GET',
url:Constants.URLConst+"/UserProfile",
headers:Constants.headers
}).then((response)=>{
return d({
type:GET_API_DATA,
response
});
}).catch((e)=>{
console.log("e",e);
})
}
}
export const GET_QUERY_LIST='GET_QUERY_LIST';
export const loadData=()=>{
return(d)=>{
axios({
method:'GET',
url:Constants.URLConst+"/Query?pageNum=1&totalperPage=15&userid=0",
headers:Constants.headers
}).then((response)=>{
type:GET_QUERY_LIST,
response
}).catch((e)=>{
console.log(e);
})
}
}
I am calling the loadData() function in a js file something like this-
import React,{Component} from 'react';
import {loadData} from './actions';
import {connect} from 'react-redux';
export class Home extends Component{
componentDidMount(){
this.props.loadData();
}
render(){
return null;
}
}
const mapStateToProps = (state) => {
return{
resultCame: state.getApplicationData
}
}
export default connect(mapStateToProps, {
loadData: loadData
})(Home);
I have two different js files, where I am calling these two functions. While, the first one works fine, for the second one I get the error,
loadData() is not a function.
How can I call multiple functions in redux and what is the problem here??

In your second axios call you need to dispatch the action.
See updated code below
export const loadData = () => {
return (dispatch) => {
axios({
method:'GET',
url:Constants.URLConst+"/Query?pageNum=1&totalperPage=15&userid=0",
headers:Constants.headers
}).then((response)=>{
// remember to dispatch the action once a response is received
dispatch(
type:GET_QUERY_LIST,
response
);
}).catch((e)=>{
console.log(e);
});
}
}

Related

React-Redux Action: 'Dispatch' is not a function

Still getting used to Redux, first off. I have a component that should simply load data for display when the component loads. I have redux setup with the store:
//store.js
import { createStore, applyMiddleware, compose } from 'redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import root from './reducers';
const middleware = [thunk, logger];
const initState = {};
const store = createStore(
root,
initState,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
and all the reducers that I'll need in a full on combine reducers file:
//{projectFolder}/reducers/index.js
import { combineReducers } from 'redux';
import authReducer from './authReducer';
import errorsReducer from './errorReducer';
import suggestionReducer from './suggestionReducer';
import insiderReducer from './insiderReducer';
import connectionReducer from './connectionReducer';
import outsiderReducer from './outsiderReducer';
import contactReducer from './contactReducer';
import metaReducer from './metaReducer';
export default combineReducers({
auth: authReducer,
errors: errorsReducer,
suggestions: suggestionReducer,
insider: insiderReducer,
connection: connectionReducer,
outsider: outsiderReducer,
contact: contactReducer,
meta: metaReducer
});
The one that I'm interested in is the metaReducer which is the called by an action, or so it should be.
//metaReducer.js
import {GET_INSIDER_META_INFORMATION, GET_OUTSIDER_META_INFORMATION } from '../actions/types';
const initState = {
insider: {},
outsider: {}
};
export default (state = initState, { type, payload }) => {
switch (type) {
case GET_INSIDER_META_INFORMATION:
return{
...state,
insider: payload
}
case GET_OUTSIDER_META_INFORMATION:
return {
...state,
outsider: payload
}
default:
return state;
}
};
The meta reducer is just to house the information coming from the back-end and is each case of the reducer is called from the actions/meta.js file which looks like this:
//{projectfolder}/actions/meta.js
import {
GET_INSIDER_META_INFORMATION,
GET_OUTSIDER_META_INFORMATION,
POPULATE_ERRORS
} from "./types";
import Axios from "axios";
export const getMetaInsider = (dispatch) => {
return Axios.get("meta/insiders")
.then(res =>
dispatch({ type: GET_INSIDER_META_INFORMATION, payload: res.data })
)
.catch(err =>
dispatch({ type: POPULATE_ERRORS, payload: err.response.data })
);
};
export const getMetaOutsider = (dispatch) => {
return Axios.get("meta/outsiders")
.then(res => {
dispatch({ type: GET_OUTSIDER_META_INFORMATION, payload: res.data });
})
.catch(err =>
dispatch({ type: POPULATE_ERRORS, payload: err.response.data })
);
};
and My component that calls all of this is setup as below:
//{projectfolder}/components/home.js
import React, {Component} from 'react';
import {Card, CardTitle, CardSubtitle, CardBody} from 'reactstrap';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {getMetaInsider, getMetaOutsider} from '../actions/meta';
class Home extends Component{
constructor(props){
super(props);
this.state = {
insider:{},
outsider: {}
}
}
componentDidMount() {
console.log(this.props);
this.props.getMetaInsider();
this.props.getMetaOutsider();
}
render(){
let {insiders, outsiders} = this.state;
return(
<React.Fragment>
{*/ omitted as it's not really an issue right now, data is more important than layout /*}
</React.Fragment>
)
}
}
const mapState = state => {
console.log(state);
return {
insider: state.meta.insider,
outsider: state.meta.outsider
}
};
Home.propTypes = {
getMetaInsider: PropTypes.func.isRequired,
getMetaOutsider: PropTypes.func.isRequired,
insider: PropTypes.object.isRequired,
outsider: PropTypes.object.isRequired
};
export default connect(mapState, {getMetaInsider, getMetaOutsider})(Home);
So when the component loads, I get a horribly weird issue where it looks like jquery is being called, and it's imported in my App.js file for bootstrap. However, the main error is this:
"TypeError: dispatch is not a function
at http://localhost:3000/static/js/bundle.js:73524:22"
Which maps up to the .catch block of the getMetaInsider function.
You have to do something like this:
export const getMetaOutsider = () => {
return (dispatch) => {
Axios.get("meta/outsiders")
.then(res => {
dispatch({ type: GET_OUTSIDER_META_INFORMATION, payload: res.data });
})
.catch(err =>
dispatch({ type: POPULATE_ERRORS, payload: err.response.data })
);
}
};
Try this, It should work. Feedbacks are welcome.
redux-thunk handles functions passed as the argument to dispatch instead of objects.

Why is my props bringing back the action function, not the date?

I have a react app that is pulling down some data, It turns a promise so I am using Thunk. However when I log this.props, the getShift action prints out as a function.
The log returns:
{getShifts: ƒ}getShifts: ƒ ()proto: Object
Action:
import settings from '../../aws-config.js';
import Amplify, { Auth, API } from 'aws-amplify';
export const GET_STAFF_SHIFTS = 'get_staff_shifts';
export const SHIFTS_LOAD_FAIL = 'shifts_load_fail';
export const getShifts = () => dispatch => {
console.log('Fetching list of shifts for user...');
const request = API.get("StaffAPI", "/shifts", {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
})
.then(response =>
dispatch({
type: 'GET_STAFF_SHIFTS',
payload: response
})
)
.catch(err =>
dispatch({type: 'SHIFTS_LOAD_FAIL'})
)
}
reducer:
import { getShifts, GET_STAFF_SHIFTS} from '../actions';
export default function(state = {}, action) {
switch(action.type){
case GET_STAFF_SHIFTS:
return Object.assign({}, state,{
start_time: action.payload
})
default:
return state;
}
}
Action:
import React, { Component } from 'react';
import Amplify, { Auth, API } from 'aws-amplify';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {getShifts} from '../actions/index';
import settings from '../../aws-config.js';
Amplify.configure(settings);
class StaffRota extends Component {
componentWillMount() {
this.props.getShifts();
}
renderPosts(){
console.log(this.props);
return (
<div></div>
);
}
}
function MapDispatchToProps(dispatch) {
return bindActionCreators({ getShifts }, dispatch);
}
export default connect(null, MapDispatchToProps)(StaffRota);
The action creator is supposed to be a function, which is expected. So console logging this.props.getShifts will give you a function.
There are two issues here:
first thing you are dispatching the wrong action type
dispatch({type: 'GET_STAFF_SHIFTS', ... }) instead of dispatch({type: GET_STAFF_SHIFTS, ... }) which you are expecting in your reducer.
secondly you ought to use the redux state via a mapStateToProps function
function MapDispatchToProps(dispatch) {
return bindActionCreators({ getShifts }, dispatch);
}
function MapStateToProps(state) {
return {
shift: state.start_time OR state.your_reducer.start_time
}
}
export default connect(MapStateToProps, MapDispatchToProps)(StaffRota);
And use this state (that is mapped to prop) via this.props.shift.

State not updating with react redux thunk

I'm a bit new to using redux and react. I'm trying to make a simple API call with redux and having it render in react. I can see the API call working as it's in the payload in redux dev tools, but I can't seem to get it to update the state possibly in the `connect?.
actions/index
import FilmAPI from '../api/api';
export const FETCH_FILMS = 'FETCH_FILMS';
export const RECEIVE_FILMS = 'RECEIVE_FILMS';
export const receiveFilms = (films) => {
return {
type: RECEIVE_FILMS,
films
};
}
export const fetchFilmsRequest = () => {
return dispatch => {
return axios.get('https://www.snagfilms.com/apis/films.json?limit=10')
.then(response => {
dispatch(receiveFilms(response.data))
})
}
}
export default fetchFilmsRequest;
reducers/FilmReducer
import RECEIVE_FILMS from '../actions/index';
export function films (state = [], action) {
switch (action.type) {
case RECEIVE_FILMS:
return [...state, action.films];
default:
return state;
}
}
reducers/index
import { combineReducers } from 'redux';
import { films } from './FilmsReducer';
export default combineReducers({
films,
});
containers/FilmListContainer
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchFilmsRequest } from '../actions';
import FilmList from '../components/FilmList'
class FilmListContainer extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.fetchFilmsRequest();
}
render() {
return (
<div>
<FilmList films={this.props.films}/>
</div>
);
}
}
const mapStateToProps = state => ({
films: state.films
})
export default connect(mapStateToProps, {fetchFilmsRequest: fetchFilmsRequest})(FilmListContainer);
configureStore.js
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// options like actionSanitizer, stateSanitizer
}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(thunk)
);
return createStore(
rootReducer,
initialState,
enhancer
);
}
As mentioned, Redux DevTools show the films in the payload, but films still remain 0 in its state. Could anyone please point me in the right direction?
You can get updated state by subscribing store and use store.getState()
Steps:
Write subscribe function in constructor of component class.
Set state of class by store.getState().
import store from '../js/store/index';
class MyClass extends Component {
constructor(props, context) {
super(props, context);
this.state = {
classVariable: ''
}
store.subscribe(() => {
this.setState({
classVariable: store.getState().MyStoreState.storeVariable
});
});
}
}
You are close, your action needs to send the data to the store by dispatching an event which your reducer can then catch. This is done using the type attribute on the dispatch object.
https://redux.js.org/basics/actions
return fetch('https://www.snagfilms.com/apis/films.json?limit=10')
.then(response => {
dispatch({
type: RECEIVE_FILMS,
payload: response,
})
})
You then need to grab the data in your reducer and put it in the store
export function films (state = [], action) {
switch (action.type) {
case RECEIVE_FILMS:
return {
...state,
films: action.payload.films
};
default:
return state;
}
}
It looks like you just need to import your action type constant into your reducer using a named import instead of a default export.
i.e. import {RECEIVE_FILMS} from '../actions' rather than import RECEIVE_FILMS from '../actions'
Just dispatch result of resolved fetch promise like so:
if the payload is json, then:
export const fetchFilmsRequest = () => {
return dispatch => {
return fetch('https://www.snagfilms.com/apis/films.json?limit=10')
.then(response => response.json())
.then(response => {
dispatch({
type: RECEIVE_FILMS,
payload: response
})
})
}
Your reducer would need modifying slightly to:
export function films (state = [], action) {
switch (action.type) {
case RECEIVE_FILMS:
return [...action.payload]; // assuming response is jus array of films
default:
return state;
}
}

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

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>

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