I'm trying to get some information about a user and store it in redux when the user logs in. I dispatch an action to the the uid which works fine. Directly after (I've commented out where in the SignIn.js code) I try to dispatch a redux thunk action that gets a list of product images from firebase. For some reason this action isn't dispatching. Not sure why? I export the function then import the function and use connect so I have access to the dispatch method. What am I missing? Thanks!
SignIn.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { fbAuth, db } from '~/config/firebase/firebaseConfig';
import { getId, getRetailerData } from '~/redux/modules/retailer';
class SignIn extends Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
push: PropTypes.func.isRequired
}
constructor(props) {
super(props);
this.state = {
email: '',
password: ''
};
this.handleEmailChange = this.handleEmailChange.bind(this);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleEmailChange (event) {
this.setState({email: event.target.value});
}
handlePasswordChange (event) {
this.setState({password: event.target.value});
}
handleSubmit (event) {
event.preventDefault();
const email = this.state.email;
const password = this.state.password;
// Register user
fbAuth.signInWithEmailAndPassword(email, password)
.catch((error) => {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log(errorMessage);
// ...
})
.then(() => {
const user = fbAuth.currentUser;
console.log(user);
this.props.dispatch(getId(user.uid));
// This action below is not being dispatched
this.props.dispatch(getRetailerData());
this.props.push(`/dashboard`);
})
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label>Email address</label>
<input
type="email"
className="form-control"
placeholder="Enter email"
onChange={this.handleEmailChange}
/>
</div>
<div className="form-group">
<label>Password</label>
<input
type="password"
className="form-control"
placeholder="Password"
onChange={this.handlePasswordChange}
/>
</div>
<button className="btn btn-default" type="submit">Continue</button>
</form>
);
}
}
export default connect()(SignIn);
My reducer
const GET_ID = 'GET_ID';
const GET_PRODUCT_IMAGES = 'GET_PRODUCT_IMAGES';
import { db, fbAuth } from '~/config/firebase/firebaseConfig';
const initialState = {
uid: '',
name: '',
productImages: [],
inventory: {}
}
export function getId (id) {
return {
type: GET_ID,
id
}
}
function getProductImages (images) {
return {
type: GET_PRODUCT_IMAGES,
images
}
}
export function getRetailerData () {
return (dispatch, getState) => {
const user = fbAuth.currentUser;
const productImages = db.ref('users/' + user.uid + '/productImages/');
productImages.on('child_added', (data) => {
console.log(data.val());
dispatch(getProductImages(data.val()))
});
}
}
export default function retailer (state = initialState, action) {
switch (action.type) {
case GET_ID :
return {
...state,
uid: action.id
}
case GET_PRODUCT_IMAGES :
return {
...state,
productImages: action.images
}
default :
return state;
}
}
Lastly I was thinking it could be something with my Index.js?
import React from 'react';
import ReactDOM from 'react-dom';
import { App } from '~/containers';
import { createStore, applyMiddleware, combineReducers, compose } from
'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import './index.css';
import * as reducers from './redux';
const store = createStore(
combineReducers(reducers),
compose(
applyMiddleware(thunk),
window.devToolsExtension ? window.devToolsExtension() : f => f
)
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
The way I usually use the connect method with actions is like this.
const mapState = state => ({
propsIWantBoundToReduxStore: store.field,
});
const mapDispatch = {
actionMethod,
//in your case: getRetailerData
};
export default connect(mapState, mapDispatch)(MyComponent);
Then you could call this.props.getRetailerData() to fire off the action.
my getRetailerData method would look something like this:
export function getRetailerData() {
return {
type: 'GET_RETAILER_DATA'
};
}
Related
am in a fixed currently, I have my store setup, actions, and reducers, also my form component where I send the dispatch action to the reducer. I get a TypeError: Object(...) is not a function. I have tried everything I can but it's probably I've not fully understood the technology. The following are my files
Form component I connected with Redux with connect
import React, { useState } from 'react';
import { connect } from 'react-redux';
import addBrandCommission from '../redux/actions/brandCommissionActions';
const AddBrandCommission = () => {
const [formData, setFormData] = useState({
name: '',
commission: '',
});
const { name, commission } = formData;
const handleChange = (e) => setFormData({ ...formData, [e.target.name]: e.target.value });
const handleSubmit = (e) => {
console.log('hi me');
e.preventDefault();
if (formData) {
console.log(formData);
addBrandCommission({
name,
commission,
});
console.log('action is called, addBrandCommission');
setFormData('');
}
};
return (
<>
<h4>here, we will add commision here</h4>
<form method="POST" onSubmit={handleSubmit}>
<div className="form-group">
<input type="text" name="name" value={name} onChange={handleChange} placeholder="Enter brand here" required />
</div>
<div className="form-group">
<input type="decimal" name="commission" value={commission} onChange={handleChange} placeholder="Enter commission here" required />
</div>
<div className="form-group">
<button type="submit" name="addBrandComission">Add Brand and Commission</button>
</div>
</form>
</>
);
};
export default connect(null, { addBrandCommission })(AddBrandCommission);
This is my 'addForm' reducer function
import { ADD_BRANDCOMMISSION, BRANDCOMMISSION_ERRORS } from '../actions/actionTypes';
const initialState = [{
name: 'Universal',
commission: '12.8',
}];
const addBrandCommissionReducer = (state = initialState, action) => {
const { type, payload } = action;
console.log(action);
switch (type) {
case ADD_BRANDCOMMISSION:
return {
...state,
brandCommission: [
{
name: payload.name,
commission: payload.commission,
},
],
};
case BRANDCOMMISSION_ERRORS:
return {
...state,
error: payload,
loading: false,
};
default:
return state;
}
};
export default addBrandCommissionReducer;
Here is my actions
import { ADD_BRANDCOMMISSION, BRANDCOMMISSION_ERRORS } from './actionTypes';
const addBrandCommission = (brandCommission) => ({
type: ADD_BRANDCOMMISSION,
payload: { brandCommission },
});
const brandCommissionErrors = () => ({
type: BRANDCOMMISSION_ERRORS,
});
export default { addBrandCommission, brandCommissionErrors };
Here is my store
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = { };
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
compose(applyMiddleware(...middleware), // store enhancer func
// eslint-disable-next-line no-underscore-dangle
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()),
);
export default store;
Here is my actionTypes
export const ADD_BRANDCOMMISSION = 'ADD_BRANDCOMMISSION';
export const BRANDCOMMISSION_ERRORS = 'BRANDCOMMISSION_ERRORS';
export const LOGGEDIN = 'LOGGEDIN';
export const LOGIN_ERRORS = 'LOGIN_ERRORS';
export const SET_USER = 'SET_USER';
export const LOGIN_REQUEST = 'LOGIN_REQUEST';
Thank you to anyone who will one way or the other share their knowledge about how to fix this code :man_technologist: :muscle:
I console.log(action.payload)in the reducer and it returned undefined
You import action wrong way. Update like this to fix:
import { addBrandCommission } from '../redux/actions/brandCommissionActions';
I am new to react and redux, this is my first attempt at using a redux action to call an API with it returning a list of products which I can then add to the redux store for me to use in any component. So far the API call is working, and returns a list of products when I add a console.log in the then response, however when I use dispatch to call the next action which sets the type I receive the error "Unhandled Rejection (TypeError): dispatch is not a function".
Here is my Fetch.js file:
import axios from "axios";
import * as React from "react";
export function loadProducts() {
return dispatch => {
return axios.get(getApiHost() + 'rest/productComposite/loadProductsWithImages/' + getId() + '/ENV_ID').then((response) => {
dispatch(getProducts(response.data.productDtos));
})
}
}
export function getProducts(productDtos) {
return {
type: 'product',
productDtos: productDtos
}
}
function getId() {
return params().get('id');
}
function getEnv() {
let env = params().get('env');
if (!env) return 'prod';
return env;
}
function getApiHost() {
}
function params() {
return new URLSearchParams(window.location.search);
}
and my reducer.js file:
const initialState = {
loaded: 'false',
productDtos: []
}
const productReducer = (state = initialState, action) => {
switch (action.type) {
case 'product':
return {
...state,
loaded: 'true',
productDtos: action.productDtos
}
default:
return {
...state,
loaded: 'false',
productDtos: action.productDtos
};
}
}
export default productReducer;
and my index.js file:
(there is lots of messy code in this file which is why i am trying to convert the project into redux)
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App';
import * as serviceWorker from './serviceWorker';
import {createStore, compose, applyMiddleware } from 'redux';
import allReducers from './components/reducers'
import {Provider} from 'react-redux';
import thunk from "redux-thunk";
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
allReducers,
composeEnhancer(applyMiddleware(thunk)),
);
ReactDOM.render(
<React.StrictMode>
<Provider store = {store}>
<App/>
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
serviceWorker.unregister();
and my app.js file:
import React from 'react';
import './App.css';
import Header from './Header';
import Footer from './Footer';
import PageHero from './PageHero';
import Products from './Products';
import ProductDetail from "./ProductDetail";
import Cart from "./Cart";
import ErrorBoundary from "./ErrorBoundary";
import PrivacyPolicy from "./PrivacyPolicy";
import LicenceAgreement from "./LicenceAgreement";
import {loadProducts} from "./Fetch";
import {connect} from "react-redux";
class App extends React.Component {
constructor(props) {
super(props);
this.cartProductsRef = React.createRef();
this.state = {
loadedShopConfig: false,
loadedLogo: false,
productView: false,
products: null,
logoUrl: null,
lAView: false,
pPView: false
};
this.onChange = this.onChange.bind(this)
this.changeBack = this.changeBack.bind(this)
this.addToCart = this.addToCart.bind(this)
this.lAViewChange = this.lAViewChange.bind(this)
this.pPViewChange = this.pPViewChange.bind(this)
}
params() {
return new URLSearchParams(window.location.search);
}
getId() {
return this.params().get('id');
}
getEnv() {
let env = this.params().get('env');
if (!env) return 'prod';
return env;
}
getApiHost() {
}
componentDidCatch(error, info) {
// Display fallback UI
this.setState({hasError: true});
// You can also log the error to an error reporting service
console.log('error', error);
console.log('info', info);
}
changeBack() {
this.setState({
productView: false,
lAView: false,
pPView: false
});
}
onChange(event) {
this.setState({productView: true});
this.setState({selectedProduct: event})
}
addToCart(product, quantity) {
console.log('cartProductsRef', this.cartProductsRef);
if (this.cartProductsRef.current) {
this.cartProductsRef.current.addToCart(product.entityInstanceId.id, quantity, product);
}
this.setState({})
}
lAViewChange() {
this.setState({lAView: true});
}
pPViewChange() {
this.setState({pPView: true});
}
loadShopDetails() {
this.setState({...this.state, isFetching: true});
fetch(this.getApiHost() + 'rest/shopComposite/' + this.getId() + '/ENV_ID')
.then((response) => response.json())
.then((responseJson) => {
console.log('Shop Details Function Results ', responseJson);
this.setState({
shopConfig: responseJson.shopConfig,
logoUrl: responseJson.logoUrl,
currencyCode: responseJson.currencyCode,
website: responseJson.website,
twitter: responseJson.twitter,
facebook: responseJson.facebook,
instagram: responseJson.instagram,
linkedIn: responseJson.linkedIn,
youTube: responseJson.youTube,
loadedShopConfig: true,
loadedLogo: true
});
})
this.setState({...this.state, isFetching: false});
}
componentDidMount() {
this.loadShopDetails();
this.props.dispatch(loadProducts());
}
render() {
let displayProduct;
const {lAView} = this.state;
const {pPView} = this.state;
const {productView} = this.state;
const {shopConfig} = this.state;
const {logoUrl} = this.state;
const {selectedProduct} = this.state;
if (productView && !lAView && !pPView) {
displayProduct = <ProductDetail
product={selectedProduct}
addToCart={this.addToCart}
/>;
} else if (!lAView && !pPView) {
displayProduct =
<Products
shopConfig={this.state.shopConfig}
productSelectedHandler={this.onChange}
/>;
}
return (
<div id="page">
<ErrorBoundary>
<p>{this.state.productView}</p>
<Header
logoUrl={this.state.logoUrl}
itemsInCart={this.state.itemsInCart}
changeBack={this.changeBack}
currencyCode={this.state.currencyCode}
/>
{!productView && !lAView && !pPView && this.state.loadedShopConfig ?
<PageHero shopConfig={this.state.shopConfig}/> : null}
{displayProduct}
<Cart id={this.getId()}
apiHost={this.getApiHost()}
ref={this.cartProductsRef}
currencyCode={this.state.currencyCode}
/>
{lAView ? <LicenceAgreement
shopConfig={this.state.shopConfig}
/> : null }
{pPView ? <PrivacyPolicy
shopConfig={this.state.shopConfig}
/> : null }
{this.state.loadedLogo ? <Footer
logoUrl={this.state.logoUrl}
lAChange={this.lAViewChange}
pPChange={this.pPViewChange}
twitter={this.state.twitter}
facebook={this.state.facebook}
instagram={this.state.instagram}
linkedIn={this.state.linkedIn}
youTube={this.state.youTube}
website={this.state.website}
/> : null}
</ErrorBoundary>
</div>
);
}
}
function mapDispatchToProps() {
return loadProducts()
}
export default connect(mapDispatchToProps)(App);
Thank you in advance for anyone who helps me, its probably a quick fix of something that I doing wrong, although I have read many articles and watched many videos and cannot find an immediate problem.
This is the way you dispatch an action in connected component. Notice that mapStateToProps is the first argument you pass into connect function, even if it returns an empty object:
import { connect } from 'react-redux'
import { loadProducts } from './path/to/actions'
class App extends React.Component {
componentDidMount() {
this.props.loadProducts();
}
render() { ... }
}
const mapStateToProps = () => ({});
const mapDispatchToProps = { loadProducts };
export default connect(mapStateToProps, mapDispatchToProps)(App);
Probably, you didn’t connect loadProducts
I am trying to connect Primereact to my django backend project use a restframework and corsheader to connect both but i keep getting this error when trying to get or pass data.
Warning: Failed prop type: The prop addLead is marked as required in LeadsForm, but its value is undefined.Uncaught TypeError: _this.props.addLead is not a function LeadsForm.js:24
My action file
import axios from 'axios';
import { GET_LEADS, ADD_LEAD } from './types'
export const getLeads = () => dispatch => {
axios`enter code here`
.get("http://127.0.0.1:8000/api/leads/")
.then(res => {
dispatch({
type: GET_LEADS,
payload: res.data
});
}).catch(err => console.log(err));
};
// ADD LEADS
export const addLead = lead => dispatch => {
axios
.post("http://127.0.0.1:8000/api/leads/", lead)
.then(res => {
dispatch({
type: ADD_LEAD,
payload: res.data
});
}).catch(err => console.log(err));
}
LeadsForm file
import React, { Component } from 'react'
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { addLead } from '../actions/leads';
import {InputTextarea} from 'primereact/inputtextarea';
import {InputText} from 'primereact/inputtext';
import { Button } from 'primereact/button';
export class LeadsForm extends Component {
state = {
name: '',
email: '',
message: ''
};
static propTypes = {
addLead: PropTypes.func.isRequired
};
onChange = e => this.setState({ [e.target.name]: e.target.value});
onSubmit = e => {
e.preventDefault();
const { name, email, message } = this.state;
const lead = { name, email, message };
this.props.addLead(lead);
this.setState({
name: "",
email: "",
message: ""
});
};
// onSubmit = e => {
// this.setState({
// name: "",
// email: "",
// message: ""
// });
// };
render() {
const { name, email, message } = this.state;
return (
<div className="card card-w-title">
<div className="p-grid ">
<h2>Add Lead</h2>
<form onSubmit={this.onSubmit}>
<div className="p-col-12">
<span className="p-float-label">
<InputText id="in"
name="name"
value={name}
onChange={this.onChange} size={50} />
<label htmlFor="in">Name</label>
</span>
</div>
<div className="p-col-12">
<span className="p-float-label">
<InputText id="in"
name="email"
value={email}
onChange={this.onChange} size={50}/>
<label htmlFor="in">Email</label>
</span>
</div>
<div className="p-col-12">
<span className="p-float-label">
<InputTextarea id="in" size={50} rows={5} cols={30}
name="message"
value={message}
onChange={this.onChange} />
<label htmlFor="in">Message</label>
</span>
</div>
<Button type = "submit" value="Submit" label="Save" style={{marginBottom:'10px'}} className="p-button-raised" />
{/* <button type="submit" className="btn btn-primary">
Submit
</button> */}
</form>
</div>
</div>
)
}
}
// LeadsForm.propTypes= {
// addLead: PropTypes.func
// }
export default connect(null, { addLead })(LeadsForm);
reducer/leads.js
import { GET_LEADS, ADD_LEAD } from '../actions/types';
const initialState = {
leads: []
}
export default function (state = initialState, action){
switch(action.type){
case GET_LEADS:
return {
...state,
leads: action.payload
};
case ADD_LEAD:
return {
...state,
leads: [...state.leads, action.payload]
}
default:
return state;
}
}
'
reducer/index.js
' import { combineReducers } from 'redux';
import leads from './leads';
export default combineReducers({
leads
});
store.js
import { createStore, applyMiddleware} from 'redux';
import { composeWithDevTools} from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
You are doing both a named export (export class ...) and a default export (export default ...) of your LeadsForm component. The default export will be wrapped by connect with your action added to props, the named export will not be wrapped by connect making your action undefined.
If the component will not work without being put through connect you should probably remove the named export to reduce confusion.
Verify that you are importing LeadsForm as a default import. This is most likely your problem.
I was moving actions from the sign in component to the context (I configured context API) everything was going fine until i had to install graphql and moved the mutations files from the sign in component to the context file. Right now is giving me the following error:
Argument of undefined passed to parser was not a valid GraphQL DocumentNode. You may need to use 'graphql-tag' or another method to convert your operation into a document
this error has came out when i used when i call the mutation at the bottom of my AppProvider component. i'm thinking that is not that way with context to use graphql mutations.
AppProvider:
// React
import React, { Fragment } from 'react';
// React apollo
import { graphql } from 'react-apollo';
import * as compose from 'lodash.flowright';
// React router
import { withRouter, Redirect } from 'react-router-dom';
// Mutation
import mutations from './mutations';
// Context
export const Context = React.createContext();
class AppProvider extends React.Component {
constructor(props) {
super(props);
this.state = {
// Login & logout states
login_credentials: {},
isLogged: false,
get_data: this.get_data,
submit: this.submit,
}
}
// Actions
// Login & Logout
get_data = async(e) => {
e.preventDefault();
const { name, value } = e.target;
const data = { [name]: value };
const newData = { ...this.state.login_credentials, ...data };
this.setState({
login_credentials: newData
});
}
submit = async(e) => {
e.preventDefault();
const { signinUser } = this.props;
const { login_credentials, isLogged } = this.state;
try {
let variables = login_credentials;
const response = await signinUser({variables});
const get_token = response.data.signinUser.token;
// setting localStorage
localStorage.setItem('token', get_token);
this.setState({
isLogged: true
});
this.props.history.push({pathname: '/home', state: {isLogged: this.state.isLogged}});
} catch(error) {
console.log(error);
}
}
// END LOGIN & LOGOUT ACTIONS
render() {
return(
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
);
}
}
export default compose(
withRouter,
graphql(mutations.signinUser, { name: 'signinUser' }),
)(AppProvider)
Sign in component where i used to use the functions and mutations:
// React
import React, { Fragment } from 'react';
// React apollo
import { graphql } from 'react-apollo';
import * as compose from 'lodash.flowright';
// React router
import { withRouter, Redirect } from 'react-router-dom';
// import mutations
import mutations from './mutations';
// React bootstrap
import { Container, Row, Form, Button } from 'react-bootstrap';
// Import Style
import './style.css';
// Component
import NavbarLayout from '../Navbar';
// Context
import Context from '../../context/context';
class LoginForm extends React.Component {
/* state = {
login_credentials: {},
isLogged: false
}
get_data = async(e) => {
e.preventDefault();
const { name, value } = e.target;
const data = { [name]: value };
const newData = { ...this.state.login_credentials, ...data };
this.setState({
login_credentials: newData
});
}
submit = async(e) => {
e.preventDefault();
const { signinUser } = this.props;
const { login_credentials, isLogged } = this.state;
try {
let variables = login_credentials;
const response = await signinUser({variables});
const get_token = response.data.signinUser.token;
// setting localStorage
localStorage.setItem('token', get_token);
this.setState({
isLogged: true
});
this.props.history.push({pathname: '/home', state: {isLogged: this.state.isLogged}});
} catch(error) {
console.log(error);
}
}
*/
render() {
return(
<Fragment>
<Context.Consumer>
{c => {
return(
<Container>
<Form className="form-container">
<h2 className="text-center pb-4">Ingreso</h2>
<Form.Group controlId="formBasicEmail">
<Form.Control name='email' onChange={e => c.get_data(e)} type="email" placeholder="Email" />
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Control name='password' onChange={e => c.get_data(e)} type="password" placeholder="Contraseña" />
</Form.Group>
<div className="text-center">
<Button className="button-login" variant="primary" onClick={e => c.submit(e)} type="submit">
Ingresa
</Button>
</div>
</Form>
</Container>
);
}}
</Context.Consumer>
</Fragment>
);
}
}
export default compose(
/* withRouter,
graphql(mutations.signinUser, { name: 'signinUser' }), */
)(LoginForm);
I'm a beginner with React & Redux and I'm trying to set up a very simple login form & redirection.
I'll add react-router or react-router-redux later.
I don't really understand where i have to put my 'logic code' (an ajax call and a redirection).
Here is what I've write.
index.js (entry point) :
import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import App from './containers/App'
import rootReducer from './reducers/reducers'
let store = createStore(rootReducer);
let rootElement = document.getElementById('root');
render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
containers/App.js :
import { Component } from 'react'
import { connect } from 'react-redux'
import { login } from '../actions/actions'
import LoginForm from '../components/LoginForm'
class App extends Component {
render () {
const { dispatch } = this.props;
return (
<div>
<LoginForm onSubmit={(id, pass) =>
dispatch(login(id, pass))
} />
</div>
)
}
}
const mapStateToProps = (state) => {
return {
}
};
const mapDispatchToProps = (dispatch) => {
return {
}
};
export default connect(mapStateToProps)(App);
components/LoginForm.js :
import { Component, PropTypes } from 'react'
class LoginForm extends Component {
render () {
return (
<div>
<form action="#" onSubmit={(e) => this.handleSubmit(e)}>
<input type="text" ref={node => { this.login = node }} />
<input type="password" ref={node => { this.password = node }} />
<input type="submit" value="Login" />
</form>
</div>
)
}
handleSubmit(e) {
e.preventDefault();
this.props.onSubmit(this.login.value, this.password.value);
}
}
LoginForm.propTypes = {
onSubmit: PropTypes.func.isRequired
};
export default LoginForm;
reducers/root.js :
import { combineReducers } from 'redux'
import user from './user'
const rootReducer = combineReducers({
user
});
export default rootReducer;
reducers/user.js :
import { LOGIN, BAD_LOGIN, LOGOUT } from '../actions/actions'
const initialState = {
cid: null,
username: '',
logo: ''
};
const user = (state = initialState, action) => {
switch (action.type) {
case LOGIN:
const api = new loginApi; //simple version
api.login(action.login, action.password)
.done(res => {
//Right here ?
})
.fail(err => console.error(err));
return state;
case LOGOUT:
//...
return state;
default:
return state;
}
};
export default user;
actions/actions.js :
export const LOGIN = 'LOGIN';
export const LOGOUT = 'LOGOUT';
export function login(login, password) {
return {
type: LOGIN,
login,
password
}
}
following this link : http://redux.js.org/docs/advanced/AsyncActions.html
I hesitate between write my login stuff inside the reducer (but I think reducer's purpose is just to reduce the state object) or to create multiple actions with one 'main' action which call REQUEST_LOGIN and LOGIN_SUCCES / LOGIN_FAILURE for example.
Thanks.
You are correct, reducers are only for mapping data to the state. Create your async logic in the action creator. The key is to use a store enhancer to make async actions possible.
redux-thunk
redux-promise
A tutorial on async redux can be found in the redux documentation.