How to make AJAX request in redux - ajax

For all I know, I have to write request in action create. How to use a promise in action for submitting a request? I am getting data in action. Then new state is created in reducer. Bind action and reducer in connect. But I don't know how to use promise for request.
Action
import $ from 'jquery';
export const GET_BOOK = 'GET_BOOK';
export default function getBook() {
return {
type: GET_BOOK,
data: $.ajax({
method: "GET",
url: "/api/data",
dataType: "json"
}).success(function(data){
return data;
})
};
}
Reducer
import {GET_BOOK} from '../actions/books';
const booksReducer = (state = initialState, action) => {
switch (action.type) {
case GET_BOOK:
return state;
default:
return state;
}
};
export default booksReducer;
Container
How display data in container?
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import getBook from '../actions/books';
import Radium from 'radium';
import {Link} from 'react-router';
function mapStateToProps(state) {
return {
books: state.data.books,
};
}
function mapDispatchToProps(dispatch) {
return {
getBooks: () => dispatch(getBook()),
};
}
#Radium
#connect(mapStateToProps, mapDispatchToProps)
class booksPage extends Component {
static propTypes = {
getBooks: PropTypes.func.isRequired,
books: PropTypes.array.isRequired,
};
render() {
const {books} = this.props;
return (
<div>
<Link to={`/authors`}><MUIButton style="flat">All Authors</MUIButton></Link>
<ul>
{books.map((book, index) =>
<li key={index}>
<Link to={`/book/${book.name}`}><MUIButton style="flat"><div class="mui--text-black mui--text-display4">
"{book.name}"</div></MUIButton></Link>
<Link to={`/author/${book.author}`}><MUIButton style="flat"><div class="mui--text-black mui--text-display4">
{book.author}</div></MUIButton></Link>
</li>
)}
</ul>
</div>
);
}
}
export default booksPage;

Since you are already using redux you can apply redux-thunk middleware which allows you to define async actions.
Installation & usage: Redux-thunk
export function fetchBook(id) {
return dispatch => {
dispatch(setLoadingBookState()); // Show a loading spinner
fetch(`/book/${id}`, (response) => {
dispatch(doneFetchingBook()); // Hide loading spinner
if(response.status == 200){
dispatch(setBook(response.json)); // Use a normal function to set the received state
}else {
dispatch(someError)
}
})
}
}
function setBook(data) {
return { type: 'SET_BOOK', data: data };
}

You should use Async Actions described in Redux Documentation
Here an example of reducer for async action.
const booksReducer = (state = {}, action) => {
switch (action.type) {
case 'RESOLVED_GET_BOOK':
return action.data;
default:
return state;
}
};
export default booksReducer;
and then you create your Async Action.
export const getBook() {
return fetch('/api/data')
.then(response => response.json())
.then(json => dispatch(resolvedGetBook(json)))
}
export const resolvedGetBook(data) {
return {
type: 'RESOLVED_GET_BOOK',
data: data
}
}
Several Notes:
We could return Promise (instead of Object) in action by using redux-thunk middleware.
Don't use jQuery ajax library. Use other library specifically for doing that (e.g. fetch()). I use axios http client.
Remember, in redux you only use pure function in reducer. Don't make ajax call inside reducer.
Read the complete guide from redux docs.

You should be able to use dispatch inside the callback (if you pass it as an argument):
export default function getBook(dispatch) {
$.ajax({
method: "GET",
url: "/api/data",
dataType: "json"
}).success(function(data){
return dispatch({type:'GET_BOOK', data: data});
});
}
Then, pass dispatch to the action:
function mapDispatchToProps(dispatch) {
return {
getBooks: () => getBook(dispatch),
};
}
Now, you should have access to the action.data property in the reducer:
const booksReducer = (state = initialState, action) => {
switch (action.type) {
case GET_BOOK:
//action.data <--- here
return state;
default:
return state;
}
};

You might want to separate concerns, to keep action creators "pure".
Solution; write some middleware. Take this for example (using superagent).
import Request from 'superagent';
const successHandler = (store,action,data) => {
const options = action.agent;
const dispatchObject = {};
dispatchObject.type = action.type + '_SUCCESS';
dispatchObject[options.resourceName || 'data'] = data;
store.dispatch(dispatchObject);
};
const errorHandler = (store,action,err) => {
store.dispatch({
type: action.type + '_ERROR',
error: err
});
};
const request = (store,action) => {
const options = action.agent;
const { user } = store.getState().auth;
let method = Request[options.method];
method = method.call(undefined, options.url)
if (user && user.get('token')) {
// This example uses jwt token
method = method.set('Authorization', 'Bearer ' + user.get('token'));
}
method.send(options.params)
.end( (err,response) => {
if (err) {
return errorHandler(store,action,err);
}
successHandler(store,action,response.body);
});
};
export const reduxAgentMiddleware = store => next => action => {
const { agent } = action;
if (agent) {
request(store, action);
}
return next(action);
};
Put all this in a module.
Now, you might have an action creator called 'auth':
export const auth = (username,password) => {
return {
type: 'AUTHENTICATE',
agent: {
url: '/auth',
method: 'post',
resourceName: 'user',
params: {
username,
password
}
}
};
};
The property 'agent' will be picked up by the middleware, which sends the constructed request over the network, then dispatches the incoming result to your store.
Your reducer handles all this, after you define the hooks:
import { Record } from 'immutable';
const initialState = Record({
user: null,
error: null
})();
export default function auth(state = initialState, action) {
switch (action.type) {
case 'AUTHENTICATE':
return state;
case 'AUTHENTICATE_SUCCESS':
return state.merge({ user: action.user, error: null });
case 'AUTHENTICATE_ERROR':
return state.merge({ user: null, error: action.error });
default:
return state;
}
};
Now inject all this into your view logic. I'm using react as an example.
import React from 'react';
import ReactDOM from 'react-dom';
/* Redux + React utils */
import { createStore, applyMiddleware, bindActionCreators } from 'redux';
import { Provider, connect } from 'react-redux';
// thunk is needed for returning functions instead
// of plain objects in your actions.
import thunkMiddleware from 'redux-thunk';
// the logger middleware is useful for inspecting data flow
import createLogger from 'redux-logger';
// Here, your new vital middleware is imported
import { myNetMiddleware } from '<your written middleware>';
/* vanilla index component */
import _Index from './components';
/* Redux reducers */
import reducers from './reducers';
/* Redux actions*/
import actionCreators from './actions/auth';
/* create store */
const store = createStore(
reducers,
applyMiddleware(
thunkMiddleware,
myNetMiddleware
)
);
/* Taint that component with store and actions */
/* If all goes well props should have 'auth', after we are done */
const Index = connect( (state) => {
const { auth } = state;
return {
auth
};
}, (dispatch) => {
return bindActionCreators(actionCreators, dispatch);
})(_Index);
const provider = (
<Provider store={store}>
<Index />
</Provider>
);
const entryElement = document.getElementById('app');
ReactDOM.render(provider, entryElement);
All of this implies you already set up a pipeline using webpack,rollup or something, to transpile from es2015 and react, to vanilla js.

Consider using the new thunk API
export const load = createAsyncThunk(
'example/api',
async (arg, thunkApi) => {
const response = await fetch('http://example.api.com/api')
if (response.status === 200) {
const json = await response.json()
return json
},
)
Also, in the new redux template application, actions are part of the reducer/slice, and you can use extraReducers to response to events related to the async action status. It is much simpler using redux this way.
See documentation of async thunk here: https://redux.js.org/usage/writing-logic-thunks

Related

Accessing useAuth0 hook data via redux thunk action with axios instance

Have a bit of an issue attempting to get Auth0 info on the logged-in user with our current architecture.
We have redux with #reduxjs/toolkit & react-redux as our state management tool.
We use axios to make HTTP requests via redux-thunk actions.
And now we have a part of our application that allows users to signup/login with Auth0.
So, an example of our problem.
Currently our redux store is setup with some reducers
/* eslint-disable import/no-cycle */
import { configureStore } from '#reduxjs/toolkit';
import thunk from 'redux-thunk';
const createStore = (initialState?: any) => {
return configureStore({
reducer: {
// reducers are here
},
middleware: [thunk],
preloadedState: initialState,
});
};
export default createStore;
Then we attached that to a Provider at the base of our application
import React from 'react';
import { Provider } from 'react-redux';
import createStore from '../store/createStore';
const App = () => {
return (
<Provider store={createStore()}>
//
</Provider>
);
};
export default App;
We have an axios instance function that uses axios to make HTTP requests and handles errors.
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import { getAuthSignature } from '../utils/auth';
export const API_URL = process.env.API_HOST;
const axiosInstance = async <T = any>(requestConfig: AxiosRequestConfig): Promise<AxiosResponse<T>> => {
const { token } = await getAuthSignature();
// I need to access auth0 data here
const { getAccessTokenSilently, isAuthenticated, isLoading, loginWithRedirect, user } = auth0;
if (!token) {
const tokenErr = {
title: 'Error',
message: 'Missing Authentication Token',
success: false,
};
throw tokenErr;
}
try {
let accessToken = token;
// Update authorization token if auth0 user
if(auth0) {
if(isAuthenticcation && user) accessToken = await getAccessTokenSilently({ audience });
else loginWithRedirect();
}
const result = await axios({
...requestConfig,
headers: {
...requestConfig.headers,
authorization: `Bearer ${accessToken}`,
},
});
return result;
} catch (error: any) {
if (error.response) {
if ([401, 403].includes(error.response.status)) {
window.location = '/';
}
const contentType = error?.response?.headers?.['content-type'];
const isHTMLRes = contentType && contentType.indexOf('text/html') !== -1;
const errObj = {
status: error?.response?.status,
statusText: error?.response?.statusText,
errorMessage: isHTMLRes && error?.response?.text && (await error?.response?.text()),
error,
};
throw errObj;
}
throw error;
}
};
export default axiosInstance;
This in an example of a thunk action, we would have something like this that uses the axios instance mentioned above to make the HTTP requests.
import axios, { API_URL } from '../../services/axios';
import { Result } from '../../types/test';
import { AppThunk } from '../../store/store';
import { setResults, setResultsLoading, setTableLoading } from './test.slice';
type DefaultThunk = () => AppThunk<Promise<void>>;
const getResults: DefaultThunk = () => async () => {
dispatch(setTableLoading(true));
try {
const result = await axios<Result[]>(
{
method: 'GET',
url: `${API_URL}/test`,
},
);
dispatch(setResults(result.data));
} catch (err: any) {
console.log({ err });
} finally {
dispatch(setResultsLoading(false));
dispatch(setTableLoading(false));
}
};
export default getResults;
We then dispatch our thunk actions to make HTTP requests and update reducer states in our React components.
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import getResults from '../../reducers/test/test.thunk';
const TestComponent = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getResults());
}, []);
return (
//
);
};
export default TestComponent;
My problem is that I have no idea how to integrate Auth0 gracefully into the current flow, so I do not have to make checks in every react component that uses a thunk action.
Basically I need access to values within the useAuth0 hook from #auth0/auth0-react for example getAccessTokenSilently, isAuthenticated, user & loginWithRedirect. Just to name a few.
We can't use the useAuth0 hook in the axios instance file, as it's not a react component/hook, nor is the thunk file.
So I'm not sure how and where the best place is to get the data so that it is accessible in the axios file, as aforementioned without having to pass it as an argument or something in every redux thunk action.
Perhaps we just need a different approach to the current flow of dispatch > action > axios request?
Is there any way to pass this data in as middleware to redux?
Any help would be greatly appreciated.
I don't believe you'd be able to use a middleware to "sniff" out the auth0 context value because middlewares run outside React. What I'd suggest here is to create a wrapper component that sits between the Auth0Provider and redux Provider components that accesses the auth0 context and dispatches an action to save it into the redux state where it can be selected via useSelector or accessed directly from store.getState().
Fortunately it appears the auth0 context value is already memoized here so it should be able to be directly consumed as a stable reference within the app.
Rough Example:
import { useDispatch } from 'react-redux';
import { useAuth0 } from '#auth0/auth0-react';
import { actions } from '../path/to/auth0Slice';
const Auth0Wrapper = ({ children }) => {
const dispatch = useDispatch();
const auth0 = useAuth0();
useEffect(() => {
dispatch(actions.setAuthContext(auth0));
}, [auth0]);
return children;
};
Create and export the store for consumption within the app.
Store
import { configureStore } from '#reduxjs/toolkit';
import thunk from 'redux-thunk';
import { combineReducers } from 'redux';
...
import auth0Reducer from '../path/to/auth0Slice';
...
const rootReducer = combineReducers({
auth0: auth0Reducer,
... other root state reducers ...
});
const createStore = (initialState?: any) => {
return configureStore({
reducer: rootReducer,
middleware: [thunk],
preloadedState: initialState,
});
};
export default createStore;
App
import Auth0Wrapper from '../path/to/Auth0Wrapper';
import createStore from '../path/to/store';
const store = createStore();
const App = () => {
return (
<Auth0Provider ......>
<Provider store={store}>
<Auth0Wrapper>
// ... JSX ...
</Auth0Wrapper>
</Provider>
</Auth0Provider>
);
};
export store;
export default App;
Create a new Auth0 state slice.
import { createSlice } from '#reduxjs/toolkit';
const auth0Slice = createSlice({
name: 'auth0',
initialState: {},
reducers: {
setAuthContext: (state, action) => {
return action.payload;
},
},
});
export const actions = {
...auth0Slice.actions,
};
export default auth0Slice.reducer;
From here you can import the exported store object and access the current state inside the axios setup.
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import store from '../path/to/App';
import { getAuthSignature } from '../utils/auth';
export const API_URL = process.env.API_HOST;
const axiosInstance = async <T = any>(requestConfig: AxiosRequestConfig): Promise<AxiosResponse<T>> => {
const { token } = await getAuthSignature();
const { auth0 } = store.getState(); // <-- access current state from store
const {
getAccessTokenSilently,
isAuthenticated,
isLoading,
loginWithRedirect,
user
} = auth0;
...
};
The hook methods are great if you're not using redux, but since you are, the recommended approach is to use the spa js library - https://github.com/auth0/auth0-spa-js/.
Here's a code example for a rest call:
document.getElementById('call-api').addEventListener('click', async () => {
const accessToken = await auth0.getTokenSilently();
const result = await fetch('https://myapi.com', {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`
}
});
const data = await result.json();
console.log(data);
});
https://github.com/auth0/auth0-spa-js/blob/master/EXAMPLES.md#calling-an-api
This is easily adaptable to thunks, in your case, inside of your axios instance ie:
const axiosInstance = async <T = any>(requestConfig: AxiosRequestConfig): Promise<AxiosResponse<T>> => {
const accessToken = await auth0.getTokenSilently();
// handle token and request
}
The auth0 with hooks is more like a convenience library, but it's built on top of spa js.

[updated]Intergrating NextJS and Redux State Management

this my updated version of intergrating redux and NextJS. Just to elobarate what I have done so far...
STEP 1. I've created a store.js file to set up my global store in reference to github's explanation from nextJS developers.
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { createWrapper, HYDRATE } from 'next-redux-wrapper';
import thunkMiddleware from 'redux-thunk';
import { customerListReducer } from './customerReducers';
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension');
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
const combinedReducer = combineReducers({
customerList: customerListReducer,
});
const reducer = (state, action) => {
console.log('Just Displaying the Store', state);
if (action.type === HYDRATE) {
const nextState = {
...state, // use previous state
...action.payload, // apply delta from hydration
};
if (state.count) nextState.count = state.count; // preserve count value on client side navigation
return nextState;
} else {
return combinedReducer(state, action);
}
};
// create a makeStore function
const store = () =>
createStore(
reducer,
bindMiddleware([thunkMiddleware])
);
// export an assembled wrapper
export const wrapper = createWrapper(store);
STEP 2: Imported the wrapper above in my _app file to make the wrapper available across all pages in my application
import Nav from '../components/Nav';
import {wrapper} from '../reducers/store';
function MyApp({ Component, pageProps }) {
return (
<>
<Nav />
<Component {...pageProps} />
</>
);
}
export default wrapper.withRedux(MyApp);
STEP 3: CONFIGURATIONS
A) My Action that calls external API
import axios from 'axios';
import {
CUSTOMER_LIST_REQUEST,
CUSTOMER_LIST_SUCCESS,
CUSTOMER_LIST_FAIL,
} from '../constants/customerConstants';
export const listCustomers = () => async (dispatch) => {
try {
dispatch({
type: CUSTOMER_LIST_REQUEST,
});
const { data } = await axios.get(
'https://byronochara.tech/gassystem/api/v1/customers'
);
const result = data.results;
dispatch({
type: CUSTOMER_LIST_SUCCESS,
payload: result,
});
} catch (error) {
dispatch({
type: CUSTOMER_LIST_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
B)My Action Reducer
import {
CUSTOMER_LIST_REQUEST,
CUSTOMER_LIST_SUCCESS,
CUSTOMER_LIST_FAIL,
} from '../constants/customerConstants';
import { HYDRATE } from 'next-redux-wrapper';
export const customerListReducer = (state = { customers: [] }, action) => {
switch (action.type) {
case HYDRATE:
return { loading: true, customers: [] };
case CUSTOMER_LIST_REQUEST:
return { loading: true, customers: [] };
case CUSTOMER_LIST_SUCCESS:
return {
loading: false,
customers: action.payload,
};
case CUSTOMER_LIST_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
C)The finally bringing it all together in my index.js page to display the results:
import React, { useEffect } from 'react';
import Head from 'next/head';
import { useSelector} from 'react-redux';
import { listCustomers } from './../actions/customerActions';
import { wrapper } from '../reducers/store';
import styles from '../styles/Home.module.css';
const Home = () => {
//Select the loaded customers' list from central state
const customerList = useSelector((state) => {
console.log(state);
return state.customerList;
});
const { loading, error, customers } = customerList;
//displaying the customers data from the external API
console.log('Fetched Customers Data', customers);
return (
<div className={styles.container}>
<Head>
<title>Home | Next</title>
</Head>
<h1>Welcome to Home Page</h1>
{/* {loading && <h6>Loading...</h6>} */}
{/* {error && <h6>Error Occured...</h6>} */}
{/* {customers.map((customer) => (
<h3>{customer.customerName}</h3>
))} */}
{/* <ArticleList customers={customers} /> */}
</div>
);
};
// getStaticProp at build time
// getServerSideProp at every request slower
// getStaticPath to dynamically generate paths based on the data we are fetching
export const getStaticProps = wrapper.getServerSideProps(async ({ store }) => {
// console.log('STORE', store);
store.dispatch(listCustomers());
});
export default Home;
COMMENT ON THE PROBLEM I'M FACING FROM THE ABOVE CODE: once everything has been set up if you follow the code above, the code seems to run well the store is successfully created when I log the result on the console ``{ customerList: { loading: true, customers: [] } }. But then I guess this is the result from the HYDRATE action type since it will always be dispatch since am using getStaticProps``` that creates a new store instance in the server.
MAIN QUIZ: My challenge is how do I bypass the HYDRATED action and reconcile the server side state with the client side store and persist it and at least to finally be able to view the list from the external API. Thanks in advance. :)
I totally recommend you to use reduxjs/toolkit. It's very simple , less code, no wrappers, clean. And no matter your project on nextjs or created via CRA. Also you dont need to configure redux-thunk and redux-devtools cause they are enabled by default. Read documentation for more information ( how to persist state without any npm package and so on )
Here is a little example.
store.js
import { combineReducers, configureStore } from "#reduxjs/toolkit";
import userSlice from './user.slice.js';
//reducers
const rootReducer = combineReducers({
user: userSlice
});
const store = configureStore({
reducer: rootReducer,
});
export default store;
Wrap with Provider (in your case _app.js)
<Provider store={store}>
<Component {...pageProps} />
</Provider>
user.slice.js ( action + reducer )
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit';
const initialState = {
id: '',
email: '',
roles: []
};
// export async action
export const signIn = createAsyncThunk('user/signIn', async (data) => {
try {
const payload = await api.auth.signin(data).then((res) => res.data);
// do some stuff if you want
return payload ;
} catch (err) {
console.log(err.response);
}
});
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
removeUser(state, payload) {
//cant be an async method
return initialState;
},
extraReducers: (builder) => {
builder.addCase(signIn.fulfilled, (state, { payload }) => {
// payload from the async method above (asyncThunk)
return payload;
});
},
},
});
// export actions
export const { removeUser } = userSlice.actions;
// export reducer
export default userSlice.reducer;
Thats it. Last step to call actions from any component e.g.
import { useDispatch, useSelector } from 'react-redux';
import { signIn, removeUser } from '../actions/userSlice';
// in function component
// call hooks
const dispatch = useDispatch();
// read the store
const { user } = useSelector((state) => state);
// dispatch any action , example below
dispatch(signIn(userCredentials));
// or
dispatch(removeUser());
I has an Issue with setting Redux with NextJS and this is my final answer after some insight from mirik999 too.
A. my store.
import { configureStore } from '#reduxjs/toolkit';
//importing the slice file with sliced reducers
import customerListReducer from '../slices/customerSlice';
// const composedEnhancer = composeWithDevTools(applyMiddleware(thunkMiddleware));
const store = configureStore({
reducer: {
customerList: customerListReducer,
},
});
export default store;
B. The store is provided in my app component
function MyApp({ Component, pageProps }) {
return (
<Provider store={store}>
<Nav />
<Component {...pageProps} />
</Provider>
);
}
export default MyApp;
C. The Slice file that automatically creates action creators and the reducer
import { createSlice } from '#reduxjs/toolkit';
//creating and action that calls API from a REST API backend
export const customersFetchedList = createAsyncThunk(
'customersList/customersListSuccess',
async () => {
try {
const { data } = await axios.get(
'https://example.com/api/your/endpoint'
);
const result = data.results;
//the payload
const payload = result;
return payload;
} catch (error) {
console.log(error.response);
const payload =
error.response && error.response.data.message
? error.response.data.message
: error.message;
return payload;
}
}
);
const initialState = {
loading: true,
customers: [],
error: false,
};
const customerListSlice = createSlice({
name: 'customersList',
initialState,
reducers: {
//reducer functions we've provided
customersRequest(state, action) {
if (state.loading == true) {
return state;
}
},
},
extraReducers: (builder) => {
initialState,
builder.addCase(customersFetchedList.fulfilled, (state, action) => {
state.loading = false;
state.customers = action.payload;
state.error = false;
return state;
});
},
});
export const {
customersRequest,
customersLoadingError,
} = customerListSlice.actions;
export default customerListSlice.reducer;
D. Then finally fired this action above in my component using the useEffect()
import React, { useEffect } from 'react';
import Head from 'next/head';
const Home = () => {
//method to fire the action
const dispatch = useDispatch();
//Select the loaded customers' list from central state
const customerList = useSelector((state) => state);
// const { loading, error, customers } = customerList;
useEffect(() => {
dispatch(listCustomers());
}, []);
return (
<div className={styles.container}>
<Head>
<title>Home | Next</title>
</Head>
<h1>Welcome to Home Page</h1>
{loading && <h6>Loading...</h6>}
{error && <h6>Error Occured...</h6>}
{customers.map((customer) => (
<h3>{customer.customerName}</h3>
))}
</div>
);
};
Thanks so much for your contribution. :)

React / Redux return object not what expected

Probably something simple...I know I am missing something...
My user id is returning as the key instead of the value. This is a test to see what was returned from the api call.
actionCreator
import * as actions from "./types";
import axios from "axios";
export const userDashBoard = userId => dispatch => {
axios.post("/api/authpages/dashboard", userId).then(user => {
dispatch({
type: actions.GET_PROFILE,
payload: user.data
});
});
};
Reducer
import { GET_PROFILE, CLEAR_PROFILE } from "../actions/types";
const INITIAL = {};
export default (state = INITIAL, action) => {
switch (action.type) {
case GET_PROFILE:
return { ...state, profileData: action.payload };
case CLEAR_PROFILE:
return { ...state, profile: "" };
default:
return state;
}
};
API
router.post("/dashboard", (req, res) => {
res.json(req.body);
});
What I get from the return in Redux
This should be an image of the redux result
I know that the api should return req.body.userId, but when I do that I get nothing. The only way I can get a response is to just call req.body...
Any help would be great...Thank you!

Redux action ajax result not dispatched to reducer

I just get to experiment with Redux and I know that middleware is essential to make ajax calls. I've installed redux-thunk and axios package separately and tried to hook my result as a state and render the ajax result to my component. However my browser console displays an error and my reducer couldn't grab the payload.
The error:
Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.
This is part of my code and how the middleware is hooked up:
//after imports
const logger = createLogger({
level: 'info',
collapsed: true,
});
const router = routerMiddleware(hashHistory);
const enhancer = compose(
applyMiddleware(thunk, router, logger),
DevTools.instrument(),
persistState(
window.location.href.match(
/[?&]debug_session=([^&]+)\b/
)
)
// store config here...
my action:
import axios from 'axios';
export const SAVE_SETTINGS = 'SAVE_SETTINGS';
const url = 'https://hidden.map.geturl/?with=params';
const request = axios.get(url);
export function saveSettings(form = {inputFrom: null, inputTo: null}) {
return (dispatch) => {
dispatch(request
.then((response) => {
const alternatives = response.data.alternatives;
var routes = [];
for (const alt of alternatives) {
const routeName = alt.response.routeName;
const r = alt.response.results;
var totalTime = 0;
var totalDistance = 0;
var hasToll = false;
// I have some logic to loop through r and reduce to 3 variables
routes.push({
totalTime: totalTime / 60,
totalDistance: totalDistance / 1000,
hasToll: hasToll
});
}
dispatch({
type: SAVE_SETTINGS,
payload: { form: form, routes: routes }
});
})
);
}
}
reducer:
import { SAVE_SETTINGS } from '../actions/configure';
const initialState = { form: {configured: false, inputFrom: null, inputTo: null}, routes: [] };
export default function configure(state = initialState, action) {
switch (action.type) {
case SAVE_SETTINGS:
return state;
default:
return state;
}
}
you can see the state routes has size of 0 but the action payload has array of 3.
Really appreciate any help, thanks.
It looks like you have an unnecessary dispatch in your action, and your request doesn't look to be instantiated in the correct place. I believe your action should be:
export function saveSettings(form = { inputFrom: null, inputTo: null }) {
return (dispatch) => {
axios.get(url).then((response) => {
...
dispatch({
type: SAVE_SETTINGS,
payload: { form: form, routes: routes }
});
});
};
}

How to get ajax request in redux?

I'm stupid, I still can't get ajax request in redux. I don't understand, where should I get getState in action. In component, I using connect that link action and reducer. Then I using componentDidMount that call an action in a component. How to get ajax request in redux from server? Help me to understand this disorder. I tried to understand the examples of redux, but it's has no effect. If I start a server, get warning : getDefaultProps is only used on classic React.createClass definitions. Use a static property named defaultProps instead.
Action
import $ from 'jquery';
export const GET_BOOK_SUCCESS = 'GET_BOOK_SUCCESS';
export default function getBook() {
return (dispatch, getState) => {
$.ajax({
method: "GET",
url: "/api/data",
dataType: "application/json"
}).success(function(result){
return dispatch({type: GET_BOOK_SUCCESS, result});
});
};
}
Reducer
import {GET_BOOK_SUCCESS} from '../actions/books';
const booksReducer = (state = {}, action) => {
console.log(action.type)
switch (action.type) {
case GET_BOOK_SUCCESS:
return Object.assign({}, state, {
books: action.result.books,
authors: action.result.authors
});
default:
return state;
}
};
export default booksReducer;
component
function mapStateToProps(state) {
console.log(state)
return {
books: state.books,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({getBooks: () => getBook(),}, dispatch);
}
#Radium
#connect(mapStateToProps, mapDispatchToProps)
class booksPage extends Component {
static propTypes = {
getBooks: PropTypes.func.isRequired,
};
componentDidMount() {
const { getBooks } = this.props;
getBooks();
}
render() {
const {books} = this.props;
index.js
const store = configureStore({}, routes);
ReactDOM.render((
<div>
<Provider store={ store }>
<ReduxRouter />
</Provider>
<DebugPanel top right bottom>
<DevTools
store={ store }
monitor={ LogMonitor }
visibleOnLoad />
</DebugPanel>
</div>),
document.getElementById('root')
);
configureStore
function configureStore(initialState, routes) {
const store = compose(
applyMiddleware(
promiseMiddleware,
thunk,
logger
),
reduxReactRouter({ routes, history }),
devTools()
)(createStore)(rootReducer, initialState);
if (module.hot) {
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers');
store.replaceReducer(nextRootReducer);
});
}
return store;
}
export default configureStore

Resources