I am learning react-redux async actions with redux-thunk, I would like to fetch data from API (my local database), Unfortunately fetching data with redux-thunk middleware data is not fetched but without thunk middleware data is fetched.
So here are action creators with thunk middleware, which is not working
// retriev comments
export const fetchComments= () =>{
return dispatch =>{
dispatch(fetchCommentsRequest);
axios.get('/api/v1/todo')
.then(response =>{
const comments =response.data;
dispatch(fetchCommentsSucces(comments))
})
.catch(error =>{
const erroMsg =errors.messages
dispatch(fetchCommentsFailure(error))
})
}
}
And here is console log result :
Here is a component where I am calling the function to fetch data from API,
import React, {useEffect}from 'react'
import { fetchComments} from '../store/actions'
import { connect } from "react-redux";
function Dashboard(userComments) {
useEffect(() =>{
fetchComments();
}, [])
return (
<div>
<p>Fetching data</p>
</div>
)
}
const mapStateToProps = state => {
console.log("I am state", state);
return {
isAuthenticated: state.Auth.isAuthenticated,
user: state.Auth.user,
userComments: state.comments
};
};
const mapDispatchToProps = dispatch => {
return {
fetchComments: () => dispatch(fetchComments()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
The whole store can be found here: store
Can someone tells me why data is not fetched?
There is an issue with how fetchComments method is called inside the <Dashboard> component.
Once a React component is connected to a Redux store, the data from the store (mapStateToProps) and the functions it can use to dispatch actions to the store (mapDispatchToProps) are passed to that component as an object.
The <Dashboard> component receives this props object that can be accessed inside it like:
function Dashboard(props) {
useEffect(() =>{
props.fetchComments();
}, [])
return (
<div>
<p>Fetching data</p>
</div>
)
}
or using destructuring:
function Dashboard({ isAuthenticated, user, userComments, fetchComments }) {
useEffect(() =>{
fetchComments();
}, [])
return (
<div>
<p>Fetching data</p>
</div>
)
}
In your thunk, dispatch the action properly i.e. call the fetchCommentsRequest function (you are providing reference)
export const fetchComments= () =>{
return dispatch =>{
dispatch(fetchCommentsRequest()); //<-----call the fuction
axios.get('/api/v1/todo')
.then(response =>{
const comments =response.data;
dispatch(fetchCommentsSucces(comments))
})
.catch(error =>{
const erroMsg =errors.messages
dispatch(fetchCommentsFailure(error))
})
}
}
In your repo, the fetchCommentsSucces needs to take an argument.
export function fetchCommentsSucces(comments){ //<----pass argument i.e comments
console.log('success')
return{
type: ActionTypes.FETCH_COMMENTS_SUCCESS,
payload: comments //<----provide correct payload
}
}
Related
I'm trying to create a component that allows me to access API data anywhere I import it.
I have a GetTeamsByLeague component which looks like this.
import axios from 'axios';
const GetTeamsByLeague = (league: string) => {
const teams = axios
.get(`http://awaydays-api.cb/api/teams/${league}`)
.then((response: any) => {
return response;
})
.catch((error: any) => {
console.log(error);
});
return teams;
};
export default GetTeamsByLeague;
Then in my app component, I have this
import Header from './Components/Header/Header';
import GetTeamsByLeague from './Hooks/GetTeamsByLeague';
import './Reset.scss';
function App() {
const championshipTeams = GetTeamsByLeague('Championship');
console.log(championshipTeams);
return (
<div className='App'>
<Header/>
</div>
);
}
export default App;
The issue is the console.log() just return the promise not the data.
If I use useState() in my GetTeamsByLeague component like this
import axios from 'axios';
import { useState } from 'react';
const GetTeamsByLeague = (league: string) => {
const [teams, setTeams] = useState({});
axios
.get(`http://awaydays-api.cb/api/teams/${league}`)
.then((response: any) => {
setTeams(response.data);
})
.catch((error: any) => {
console.log(error);
});
return teams;
};
export default GetTeamsByLeague;
Then I get the following errors
(3) [{…}, {…}, {…}] ...
GET http://awaydays-api.cb/api/teams/Championship 429 (Too Many Requests)
/* EDIT */
I've now updated my GetTeamsByLeague component too
import axios from 'axios';
import { useEffect, useState } from 'react';
interface Teams {
id: number;
name: string;
league: string;
created_at: string;
updated_at: string;
}
const useGetTeamsByLeague = (league: string) => {
const [teams, setTeams] = useState<Teams[]>();
useEffect(() => {
axios
.get(`http://awaydays-api.cb/api/teams/${league}`)
.then((response: any) => {
setTeams(response.data);
})
.catch((error: any) => {
console.log(error);
});
}, [league, setTeams]);
return teams;
};
export default useGetTeamsByLeague;
In my component done
import useGetTeamsByLeague from './Hooks/useGetTeamsByLeague';
import './Reset.scss';
function App() {
const teams = useGetTeamsByLeague('Championship');
console.log(teams);
return (
<div className='App'>
<Header>
{teams.map(team => {
<li>{team.name}</li>;
})}
</Header>
</div>
);
}
export default App;
But now I get TypeScript error Object is possibly 'undefined'
The console.log shows empty array first then the data
undefined
(3) [{…}, {…}, {…}]
In the first case, you return a Promise since you don't await the axios fetch. In the second case, after the axios fetch succeeds, you store the result in the state which re-renders the component, which causes an infinite loop of fetching -> setting state -> re-rendering -> fetching [...].
This is a perfect use case for a React hook. You could do something like this:
const useGetTeamsByLeague = (league) => {
const [status, setStatus] = useState('idle');
const [teams, setTeams] = useState([]);
useEffect(() => {
if (!league) return;
const fetchData = async () => {
setStatus('fetching');
const {data} = await axios.get(
`http://awaydays-api.cb/api/teams/${league}`);
if(data && Array.isArray(data)){
setTeams(data);
}
setStatus('fetched');
};
fetchData();
}, [league]);
return { status, teams };
};
And then inside your components do
const {status, teams} = useGetTeamsByLeague("someLeague");
Ofc. you should modify the hook to your needs since I don't know how your data structure etc. looks like.
A component function is run on every render cycle, therefore the request is many times. You should use the useEffect() hook (documentation).
Wrapping this logic in a component is probably not the right tool to use in this case. You should probably consider a custom hook instead, for example:
const useGetTeamsByLeague = (league: string) => {
const [teams, setTeams] = useState({});
useEffect(() => {
axios
.get(`http://awaydays-api.cb/api/teams/${league}`)
.then((response: any) => {
setTeams(response.data);
})
.catch((error: any) => {
console.log(error);
});
}, [league, setTeams]);
return teams;
};
Need help in getting response from a function written inside reducer function
functional component
import {
getAssets,
} from '../../reducers';
const myFunctionalComponent = (props) => {
const dispatch = useDispatch();
const onLinkClick = () => {
dispatch(getAssets());
}
}
return (
<div>
<mainContent />
</div>
)
}
In my reducer
const reducer = (state = initialState, action) => {
switch (action.type) {
case ASSETS_LIST: {
return {
...state,
successToast: true,
isLoading: false,
data: action.payload,
};
}
}
export const listsDispactcher = () => dispatch => {
dispatch({ type: SHOW_LOADER });
performGet(ENDPOINT URL)
.then(response => {
debugger;
const payload = response.data;
dispatch({
type: ASSETS_LIST,
payload: {
...payload,
data: payload.results,
},
});
dispatch({ type: HIDE_LOADER });
})
.catch(err => {
dispatch({ type: GET_ASSETS_ERROR, payload: err });
);
});
};
when i click the link ,am getting my api called in function in reducer and its getting response in newtwork tab in developer console , but how to get the response (that is successToast,data,isLoading )in my functional component and to pass the same to child components ?
I advice you to change the structure of your project. Place all your network calls in a file and call them from your component. It is better for readability and understandability
import {
getAssets,
} from './actions';
const myFunctionalComponent = (props) => {
const dispatch = useDispatch();
const onLinkClick = async () => {
const data = await dispatch(getAssets());
}
}
return (
<div>
<mainContent />
</div>
)
}
In ./actions.js
const getAssets =()=>async dispatch =>{
const res = await axios.get();
dispatch(setYourReduxState(res.data));
return res.data;
}
Now your component will get the data of network call. and Your redux state also will get update
For functional components, to access state stored centrally in redux you need to use useSelector hook from react-redux
import React from 'react'
import { useSelector } from 'react-redux'
export const CounterComponent = () => {
const counter = useSelector(state => state.counter)
return <div>{counter}</div>
}
Official doc:
https://react-redux.js.org/api/hooks#useselector-examples
Also found this working example for you to refer.
https://codesandbox.io/s/8l0sv
I want my component to fetch an array of objects from the server. Each object is a message with author, body and date. I then want to render these messages in my react component.
My react component currently fetches data from the server before mounting. It will then store this message list in the redux state.|
I'm sure there's a better way of writing this code.
1. Can I place the fetch request in either the Action or Reducer file?
2. Can I write a function in the component to make the async call?
import React, { Component } from 'react';
import Message from '../components/message.jsx';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
// Actions
import { fetchMessages } from '../actions/actions_index.js';
class MessageList extends Component {
constructor(props) {
super(props)
}
componentWillMount() {
fetch('https://wagon-chat.herokuapp.com/general/messages')
.then(response => response.json(),
error => console.log('An error occured receiving messages', error))
.then((data) => {
this.props.fetchMessages(data.messages);
});
}
render() {
return (
<div className="message-list">
{this.props.messageList.map( (message, index) => { return <Message key={index} message={message}/> })}
</div>
)
}
}
function mapStateToProps(state) {
return {
messageList: state.messageList
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{ fetchMessages: fetchMessages },
dispatch
)
}
export default connect(mapStateToProps, mapDispatchToProps)(MessageList);
Can I place the fetch request in either the Action or Reducer file?
The fetch request should be placed in action creator. Where the retrieved data will be dispatched to reducer later to manipulate the data, and lastly update the store to show on UI. Here's simple flow for most of react-redux app.
UI -> Action creator (calling request, saga etc..) -> reducer -> store -> UI
Can I write a function in the component to make the async call?
Yes, this should be called action creator, and you can see actions.js below for more reference.
I think you can safely follow this sample pattern where most tutorials out there apply. I'm assuming all files listed here are in the same directory.
constant.js
const MESSAGE_FETCH__SUCCESS = 'MESSAGE/FETCH__SUCCESS'
const MESSAGE_FETCH__ERROR = 'MESSAGE/FETCH__ERROR'
export {
MESSAGE_FETCH__SUCCESS,
MESSAGE_FETCH__ERROR
}
actions.js
import {
MESSAGE_FETCH__SUCCESS,
MESSAGE_FETCH__ERROR
} from './constant';
const fetchMessageError = () => ({
type: MESSAGE_FETCH__ERROR
})
const fetchMessageSuccess = data => ({
type: MESSAGE_FETCH__SUCCESS,
payload: data
})
const fetchMessages = () => {
const data = fetch(...);
// if error
if (data.error)
fetchMessageError();
else fetchMessageSuccess(data.data);
}
export {
fetchMessages
}
reducers.js
import {
MESSAGE_FETCH__SUCCESS,
MESSAGE_FETCH__ERROR
} from './constant';
const INIT_STATE = {
messageList: []
}
export default function( state = INIT_STATE, action ) {
switch(action.type) {
case MESSAGE_FETCH__SUCCESS:
return {
...state,
messageList: action.payload
}
case MESSAGE_FETCH__ERROR:
// Do whatever you want here for an error case
return {
...state
}
default:
return state;
}
}
index.js
Please read the comment I noted
import React, { Component } from 'react';
import Message from '../components/message.jsx';
import { connect } from 'react-redux';
// Actions
import { fetchMessages } from './actions';
class MessageList extends Component {
/* If you don't do anything in the constructor, it's okay to remove calling `constructor(props)`
*/
//constructor(props) {
// super(props)
//}
// I usually put this async call in `componentDidMount` method
componentWillMount() {
this.props.fetchMessage();
}
render() {
return (
<div className="message-list">
{
/* Each message should have an unique id so they can be used
for `key` index. Do not use `index` as an value to `key`.
See this useful link for more reference: https://stackoverflow.com/questions/28329382/understanding-unique-keys-for-array-children-in-react-js
*/
this.props.messageList.map( message => <Message key={message.id} message={message}/> )
}
</div>
)
}
}
function mapStateToProps(state) {
return {
messageList: state.messageList
}
}
export default connect(mapStateToProps, {
fetchMessages
})(MessageList);
You could use redux-thunk in an action called getMessages.
So:
(The double arrow func, is to return an action, see redux-thunk)
const getMessages = ()=>(dispatch, getState)=>{
fetch('https://wagon-chat.herokuapp.com/general/messages')
.then(response => response.json(),
error => dispatch(['error', error]))
.then((data) => {
dispatch(data);
})
}
Then you've successfully reduced your component to:
componentWillMount(){
this.props.getMessages()
}
I think #Duc_Hong answered the question.
And in my opinion, I suggest using the side-effect middle-ware to make AJAX call more structured, so that we could handle more complicated scenarios (e.g. cancel the ajax request, multiple request in the same time) and make it more testable.
Here's the code snippet using Redux Saga
// Actions.js
const FOO_FETCH_START = 'FOO\FETCH_START'
function action(type, payload={}) {
return {type, payload};
}
export const startFetch = () => action{FOO_FETCH_START, payload);
// reducer.js
export const foo = (state = {status: 'loading'}, action) => {
switch (action.type) {
case FOO_FETCH_STARTED: {
return _.assign({}, state, {status: 'start fetching', foo: null});
}
case FOO_FETCH_SUCCESS: {
return _.assign({}, state, {status: 'success', foo: action.data});
}
......
}
};
Can I place the fetch request in either the Action or Reducer file?
// Saga.js, I put the ajax call (fetch, axios whatever you want) here.
export function* fetchFoo() {
const response = yield call(fetch, url);
yield put({type: FOO_FETCH_SUCCESS, reponse.data});
}
// This function will be used in `rootSaga()`, it's a listener for the action FOO_FETCH_START
export function* fooSagas() {
yield takeEvery(FOO_FETCH_START, fetchFoo);
}
Can I write a function in the component to make the async call?
// React component, I trigger the fetch by an action creation in componentDidMount
class Foo extends React.Component {
componentDidMount() {
this.props.startFetch();
}
render() {
<div>
{this.props.foo.data ? this.props.foo.data : 'Loading....'}
<div>
}
}
const mapStateToProps = (state) => ({foo: state.foo});
const mapDispatchToProps = { startFetch }
export default connect(mapStateToProps, mapDispatchToProps) (Foo);
//client.js, link up saga, redux, and React Component
const render = App => {
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
combinedReducers,
initialState,
composeEnhancers(applyMiddleware(sagaMiddleware))
);
store.runSaga(rootSaga);
return ReactDOM.hydrate(
<ReduxProvider store={store}>
<BrowserRouter><AppContainer><App/></AppContainer></BrowserRouter>
</ReduxProvider>,
document.getElementById('root')
);
}
I have the following React component that shows all the users posts through the "renderPosts" method. Below it there's a like/unlike button on whether the currently logged in user has liked the post.
However, when I click on the like button, the component does not re-render in order for the "renderPosts" method to create an unlike button and the "like string" is modified as expected. Only when I go to another component and then come back to this component does the unlike button display and vice versa.
Is there anyway that I could fix this with Redux in my app? I tried this.forceUpdate after the onClick event but still does not work...
Also I tried creating a new Reducer called "likers", according to robinsax which basically get the array of users who like a particular post and imported it as props into the component but got
"this.props.likers.includes(currentUser)" is not a function
When the app first gets to the main page (PostIndex), probably because this.props.likers is still an empty object returned from reducer
Here is the code for my action creator:
export function likePost(username,postId) {
// body...
const request = {
username,
postId
}
const post = axios.post(`${ROOT_URL}/likePost`,request);
return{
type: LIKE_POST,
payload: post
}
}
export function unlikePost(username,postId){
const request = {
username,
postId
}
const post = axios.post(`${ROOT_URL}/unlikePost`,request);
return{
type: UNLIKE_POST,
payload: post
}
}
And this is my reducer:
import {LIKE_POST,UNLIKE_POST} from '../actions/index.js';
export default function(state = {},action){
switch(action.type){
case LIKE_POST:
const likers = action.payload.data.likedBy;
console.log(likers);
return likers;
case UNLIKE_POST:
const unlikers = action.payload.data.likedBy;
console.log(unlikers);
return unlikers;
default:
return state;
}
}
I would really appreciate any help since I'm a beginner
import { fetchPosts } from "../actions/";
import { likePost } from "../actions/";
import { unlikePost } from "../actions/";
class PostsIndex extends Component {
componentDidMount() {
this.props.fetchPosts();
}
renderPost() {
const currentUser = Object.values(this.props.users)[0].username;
return _.map(this.props.posts, post => {
return (
<li className="list-group-item">
<Link to={`/user/${post.username}`}>
Poster: {post.username}
</Link>
<br />
Created At: {post.createdAt}, near {post.location}
<br />
<Link to={`/posts/${post._id}`}>{post.title}</Link>
<br />
//error here, with this.props.likers being an
//array
{!this.props.likers.includes(currentUser) ? (
<Button
onClick={() => this.props.likePost(currentUser,post._id)}
bsStyle="success"
>
Like
</Button>
) : (
<Button
onClick={() => this.props.unlikePost(currentUser,post._id)}
bsStyle="warning"
>
Unlike
</Button>
)}{" "}
{post.likedBy.length === 1
? `${post.likedBy[0]} likes this`
: `${post.likedBy.length} people like this`}
</li>
);
});
}
function mapStateToProps(state) {
return {
posts: state.posts,
users: state.users,
likers: state.likers
};
}
}
Seems like the like/unlike post functionality isn't causing anything in your state or props to change, so the component doesn't re-render.
You should change the data structure you're storing so that the value of post.likedBy.includes(currentUser) is included in one of those, or forceUpdate() the component after the likePost and unlikePost calls.
Please do it the first way so I can sleep at night. Having a component's render() be affected by things not in its props or state defeats the purpose of using React.
As noted in other answers, you need to use redux-thunk or redux-saga to make async calls that update you reducer. I personally prefer redux-saga. Here's is a basic implementation of React, Redux, and Redux-Saga.
Redux-Saga uses JavaScript generator functions and yield to accomplish the goal of handling async calls.
Below you'll see a lot of familiar React-Redux code, the key parts of Redux-Saga are as follows:
watchRequest - A generator function that maps dispatch actions to generator functions
loadTodo - A generator function called from watchRequest to yield a value from an async call and dispatch an action for the reducer
getTodoAPI - A regular function that makes a fetch request
applyMiddleware - from Redux is used to connect Redux-Saga with createStore
const { applyMiddleware, createStore } = Redux;
const createSagaMiddleware = ReduxSaga.default;
const { put, call } = ReduxSaga.effects;
const { takeLatest } = ReduxSaga;
const { connect, Provider } = ReactRedux;
// API Call
const getTodoAPI = () => {
return fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => {
return response.json()
.then(response => response);
})
.catch(error => {
throw error;
})
};
// Reducer
const userReducer = (state = {}, action) => {
switch (action.type) {
case 'LOAD_TODO_SUCCESS':
return action.todo;
default:
return state;
}
};
// Sagas, which are generator functions
// Note: the asterix
function* loadTodo() {
try {
const todo = yield call(getTodoAPI);
yield put({type: 'LOAD_TODO_SUCCESS', todo});
} catch (error) {
throw error;
}
}
// Redux-Saga uses generator functions,
// which are basically watchers to wait for an action
function* watchRequest() {
yield* takeLatest('LOAD_TODO_REQUEST', loadTodo);
}
class App extends React.Component {
render() {
const { data } = this.props;
return (
<div>
<button onClick={() => this.props.getTodo()}>Load Data</button>
{data ?
<p>data: {JSON.stringify(data)}</p>
: null
}
</div>
)
}
}
// Setup React-Redux and Connect Redux-Saga
const sagaMiddleware = createSagaMiddleware();
const store = createStore(userReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(watchRequest);
// Your regular React-Redux stuff
const mapStateToProps = (state) => ({ data: state }); // Map the store's state to component's props
const mapDispatchToProps = (dispatch) => ({ getTodo: () => dispatch({type: 'LOAD_TODO_REQUEST'}) }) // wrap action creator with dispatch method
const RootComponent = connect(mapStateToProps, mapDispatchToProps)(App);
ReactDOM.render(
<Provider store={store}>
<RootComponent />
</Provider>,
document.getElementById('root')
);
<script src="https://npmcdn.com/babel-regenerator-runtime#6.3.13/runtime.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.1/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/6.0.0/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux-saga/0.16.2/redux-saga.min.js"></script>
<div id="root"></div>
You need to use redux-thunk middleware in order to use async actions.
First, add redux-thunk while creating store like
import thunk from 'redux-thunk';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
then change your method like this
export function likePost(username,postId) {
return function(dispatch) {
// body...
const request = {
username,
postId
}
axios.post(`${ROOT_URL}/likePost`,request)
.then(res => {
dispatch({
type: LIKE_POST,
payload: res
});
});
}
}
and now in your component after mapStateToProps, define mapDispatchToProps,
const mapDispatchToProps = dispatch => {
return {
likePost: (currentUser,postId) => dispatch(likePost(currentUser, postId)),
// same goes for "unlike" function
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PostsIndex);
The problem is in your action creator.
export function likePost(username,postId) {
// body...
const request = {
username,
postId
}
// this is an async call
const post = axios.post(`${ROOT_URL}/likePost`,request);
// next line will execute before the above async call is returned
return{
type: LIKE_POST,
payload: post
}
}
Because of that your state is likely never updated and stays in the initial value.
You would need to use either redux-thunk or redux-saga to work with async actions.
As they say use redux-thunk or redux-saga. If your new to redux I prefer redux-thunk because it's easy to learn than redux-saga. You can rewrite your code like this
export function likePost(username,postId) {
// body...
const request = {
username,
postId
}
const post = axios.post(`${ROOT_URL}/likePost`,request);
return dispatch => {
post.then(res => {
dispatch(anotherAction) //it can be the action to update state
});
}
}
I'm trying to setup a simple form using redux-form.
Right now I just have a button, that should dispatch an action. I see the SET_SUBMIT_SUCCEEDED action so I know the form submitted successfully, and I can see the onSubmit function I specified is being called, but it is not dispatching an action.
// root reducer
import { combineReducers } from 'redux'
import { reducer as reduxFormReducer } from 'redux-form'
import login from './login'
export default combineReducers({
login,
form: reduxFormReducer
})
// action-creator
import { createAction } from './create_action'
import { postJson } from './fetch'
import {
LOGIN_ERROR,
LOGIN_REQUEST,
LOGIN_SUCCESS
} from '../constants/constants'
const login = () => {
return function (dispatch) {
dispatch(createAction(LOGIN_REQUEST))
return postJson('/login')
.then(res => res.json())
.then(data =>
dispatch(createAction(LOGIN_SUCCESS, { data }))
)
.catch(error => dispatch(createAction(LOGIN_ERROR, { error })))
}
}
//component
import React from 'react'
import { reduxForm } from 'redux-form'
import login from '../redux/submit-handlers/login'
const Login = (props) => {
const { handleSubmit } = props
return (
<form onSubmit={handleSubmit}>
<h3>Login</h3>
<button>Login</button>
</form>
)
}
export default reduxForm({
form: 'login',
onSubmit: login
})(Login)
//create_action
export const createAction = (type, payload) => {
return {
type,
payload
}
}
I haven't had any problems dispatching actions using this approach in other components. I must just have something configured wrong with redux, but I feel like I'm doing it just as the examples described.
If I pass the login function down in props and pass it to handleSubmit like this
<form onSubmit={handleSubmit(() => {login()})}>
The onSubmit function is called, but it's called before SET_SUBMIT_SUCCEEDED which makes me think that any validation I add wouldn't work, and it would submit anyway.
It seems that your login function should have a different signature.
From looking at the docs http://redux-form.com/6.2.0/docs/api/ReduxForm.md/
onSubmit will be called with the following parameters:
values : Object The field values in the form of { field1: 'value1', field2: 'value2' }.
dispatch : Function The Redux dispatch function.
props : Object The props passed into your decorated component.
Try something like:
const login = ({dispatch}) => {
dispatch(createAction(LOGIN_REQUEST))
return postJson('/login')
.then(res => res.json())
.then(data =>
dispatch(createAction(LOGIN_SUCCESS, { data }))
)
.catch(error => dispatch(createAction(LOGIN_ERROR, { error })))
}
}
This worked
const login = (values, dispatch) => {
dispatch(createAction(LOGIN_REQUEST))
return postJson('/login')
.then(res => res.json())
.then(data =>
dispatch(createAction(LOGIN_SUCCESS, { data }))
)
.catch(error => dispatch(createAction(LOGIN_ERROR, { error })))
}