I'm doing it right if I put the fetch in the componentDidMount ()? It is folly to put the fetch into the action or reducer?
Why this {this.props.data.name} does not work without setting of standard parameters for data in reducer? Without this (state = {data: { }}, action)?!
Reducer
const reducer = (state = {
data: {
}
}, action) => {
switch (action.type) {
case 'EXPERIMENT':
return {
...state,
data: action.data
}
break
default:
return state
}
}
export default reducer
Component
import React, { Component } from 'react'
import { connect } from 'react-redux'
class Persistent extends Component {
componentDidMount () {
fetch('https://api.github.com/users/reactjs').then((response) => {
response.json().then((json) => {
this.props.dispatch({
type: 'EXPERIMENT',
data: json
})
})
})
}
render () {
return (
<div>
<ol>
<li>{this.props.data.name}</li>
<li>{this.props.data.url}</li>
</ol>
</div>
)
}
}
export default connect(
(state) => {
return {
data: state.data
}
}
)(Persistent)
Use redux-thunk middleware. It allows action creators to return functions instead of action objects. The functions are chained to do a final dispatch of an action object.
While creating the store, include the middleware as follows:
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import RootReducer from '../reducers/rootReducer';
const loggerMiddleware = createLogger();
export function configureStore(initialState) {
return createStore(
RootReducer,
initialState,
applyMiddleware(
thunkMiddleware,
loggerMiddleware
));
}
An example of an action creator which uses the thunk middleware:
getCards(email, token) {
return (dispatch, getStore) => {
dispatch(CardActions.getCardsRequest(email));
fetch(apiUrls.getCardsUrl + email, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Api-Key': token,
},
})
.then(response => {
return response.json().then(responseJson => {
return dispatch(CardActions.getCardsResponse(responseJson.postcards, response.status));
});
})
.catch(err => {
console.error(err);
});
};
}
One of key constraint of redux is that reducers have to be pure functions. What it means is that it cannot have side-effects like fetching data. So in short fetch cannot go into reducer.
reducer is a function and (state = {data: { }}, action) are parameters to reducer function with the part {data: { }} being the default value for the state. Your code uses es2015 arrow functions syntax which is equivalent of plain old js:
function reducer(state = {data: { }}, action) {
switch (action.type) {
case 'EXPERIMENT':
return {
...state,
data: action.data
}
break
default:
return state
}
}
It won't work without setting default parameters because store won't have necessary objects.
Related
I want to use redux hook useSelector to access the store and get rid of connect(), so I need to create a way to export my actions, and I'm thinking on a class with static methods, here is an example
export default class AuthActions {
static async login(userData) {
try {
const user = await axios.post('http://localhost:5000', userData);
dispatch({
type: AUTH.LOGIN,
payload: user.data
})
} catch (error) {
dispatch({
type: SET_ERROR,
payload: error
})
}
}
static setUser() {
console.log("SET USER")
}
static logout() {
console.log("Logout")
}
}
And then I use the action methods as follows:
import React from 'react';
import AuthActions from '../../redux/actions/AuthActions';
import { useSelector } from 'react-redux';
export default const Login = () => {
//More logic....
const { isAuth } = useSelector((state) => state.auth);
const submitHandler = e => {
e.preventDefault();
AuthActions.login(userData)
}
return (
<form onSubmit={submitHandler}>
My Login form ....
</form>
);
};
But I'm wondering if there is a disadvantage or performance issues to use redux in this way, or should I avoid the usage of a class and use a simple object instead?
Thank you in advance
this is my format of a reducer, inspired by ducks-modular-redux
for example, check out this darkMode reducer:
export const constants = {
TOGGLE: "darkMode/TOGGLE"
};
export const actions = {
toggleDarkMode: () => {
return {
type: constants.TOGGLE
};
}
};
export const thunks = {
toggleDarkMode: () => {
return (dispatch, getState) => {
dispatch(actions.toggleDarkMode());
const isDark = getState().darkMode.isDark;
localStorage.setItem("isDark", isDark);
};
}
};
const initialState = { isDark: localStorage.getItem("isDark") === "true" };
export default (state = initialState, action) => {
switch (action.type) {
case constants.TOGGLE:
return {
isDark: !state.isDark
};
default:
return state;
}
};
I can't set the state with mapDispatchToProps and pass the payload
Imports:
import React, { Component } from 'react';
import classes from './HoursWatched.mod ule.css';
import axios from 'axios';
import { connect } from 'react-redux';
import * as actionType from '../../../store/actions';
This is how I try to trigger the action:
componentDidMount() {
this.getHoursWatched()
}
getHoursWatched() {
axios.get(API_URL, header)
.then((res) => {
console.log(res)
this.props.hoursWatched(res)
})
.catch((err) => {
console.log("Error", err)
})
}
The mapDiscpatchToProps:
const mapDispatchToProps = dispatch => {
return {
hoursWatched : (res) => dispatch({ type: actionType.HOURS_WATCHED, payload: res })
}
}
Exports:
export default connect(mapStateToProps, mapDispatchToProps)(HoursWatched);
In the reducer:
const reducer = (state = initialState, action, payload) => {
switch (action.type) {
case actionTypes.HOURS_WATCHED:
const newHoursWatched = {};
console.log(payload) //This is undefined
return {
...state,
hoursWatched: newHoursWatched
};
Why I can't pass the payload? It says it's undefined
You are trying to refer to a wrong argument in the reducer. In order to access the payload that you are passing from the API response you need to use action.payload in the reducer instead of payload.
Please help, this has been bugging me for the last four days, I can't work it out.
I'm using react and redux to pull some data down that comes in a promse
I know I've got thunk installed but I dont know how to use it.
However my action comes back: Actions must be plain objects. Use custom middleware for async actions.
Action:
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: 'SHIFTS_LOAD_SUCCESS', response})
)
.catch(err =>
dispatch({type: 'SHIFTS_LOAD_FAIL'})
)
}
Reducer:
import _ from "lodash"
import { getShifts} from '../actions';
export default function(state = {}, action) {
switch(action.type){
case SHIFTS_LOAD_SUCCESS:
return { ...state, rota: action.response };
}
}
Component:
componentWillMount() {
this.props.getShifts();
}
renderPosts(){
console.log(this.props);
return (
<div>
<button onClick={this.logOut}>LogOut</button>
<h4>hello{this.props.rotaData}</h4>
</div>
);
}
render() {
return (
<div className="row">
{this.renderPosts()}
</div>
);
}
}
function mapStateToProps(state){
return{ rota: state.response };
}
export default connect(mapStateToProps, {getShifts: getShifts}) (StaffRota);
I'm new to all of this to please forgive me if its simple. Know-one else has been able to fix my problem.
Did you setup redux-thunk when you create your store? it should be something like
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
// Create store
const store = createStore(yourReducers, applyMiddleware(thunk));
Try to double check the thunk documentation https://github.com/reduxjs/redux-thunk
Can anyone help me figure out what I'm doing wrong? I keep getting actions should be an object use custom middleware error. It works if I try to return like { type: 'SOMETHING' } on the fetchAdmins(), but according to the redux-thunk docs I should be able to return a function that has dispatch as params and that's what I did but maybe I missed something.
store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import allReducers from './js/reducers/index.js';
const Store = (initialState) =>
createStore(
allReducers,
initialState,
applyMiddleware(thunk)
);
export default Store;
RootAdmin.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Route } from 'react-router-dom';
import { fetchAdmins, addAdmin, deleteAdmin } from '../actions/actions.js';
#connect(
state => ({
admins: state.admins
}),
dispatch => bindActionCreators({
fetchAdmins: fetchAdmins,
addAdmin: addAdmin,
deleteAdmin: deleteAdmin
}, dispatch)
)
class RootAdmin extends Component {
// ...codes
componentDidMount() {
this.props.fetchAdmins();
}
// ...codes
}
};
export default RootAdmin;
actions.js
import axios from 'axios';
export function fetchAdmins() {
console.log('fired'); // this gets fired.
return (dispatch) => {
console.log('not fired'); // not fired.
dispatch({ type: 'FETCHING_ADMINS' });
console.log('fetching'); // won't even log
axios({
url: '/api/fetchAdmins'
})
.then(res =>
dispatch({ type: 'FETCHED_ADMINS', payload: res.data })
)
.catch(err =>
dispatch({ type: 'FAILED_FETCH_ADMINS' })
);
};
}
reducer-admins.js
export default function (state = null, action) {
const { payload } = action;
let newState = {...state};
switch (action.type) {
case 'FETCHING_ADMINS':
newState = {...payload};
newState.log += '\nfetching admins';
console.log('fetching admins');
return newState;
break;
}
return state;
}
Thank you very much!
It's not your action creator causing the issue... I believe the issue lies in your mapDispatchToProps
#connect(
state => ({
admins: state.admins
}),
dispatch => bindActionCreators({
fetchAdmins: fetchAdmins,
addAdmin: addAdmin,
deleteAdmin: deleteAdmin
}, dispatch)
)
Note that you're returning an object from the state mapping function, but in your dispatch you're returning the result of bindActionCreators which can be an object or a function...
#connect(
state => ({
admins: state.admins
}),
dispatch => ({
actions: bindActionCreators(Object.assign({}, fetchAdmins, addAdmin, deleteAdmin), dispatch)
})
)
then access your method as this.props.actions.fetchAdmins();
I try to deal with ajax data using axom in my learning react,redux project and I have no idea how to dispatch an action and set the state inside a component
In component will mount
componentWillMount(){
this.props.actions.addPerson();
}
Store
import { createStore, applyMiddleware } from "redux";
import rootReducer from "../reducers";
import thunk from "redux-thunk";
export default function configureStore() {
return createStore(rootReducer, applyMiddleware(thunk));
}
In Action :
import * as types from "./action-types";
import axios from "axios";
export const addPerson = person => {
var response = [];
axios
.get(`&&&&&&&&&&&`)
.then(res => {
response = res.data;
return {
type: types.ADD_PERSON,
response
};
});
};
In reducer
import * as types from "../actions/action-types";
export default (state = [], action) => {
console.log("action======>", action);
switch (action.type) {
case types.ADD_PERSON:
console.log("here in action", action);
return [...state, action.person];
default:
return state;
}
};
I am getting Actions must be plain objects. Use custom middleware for async actions.
You should use dispatch for async function. Take a look of the redux-thunk's documentation: https://github.com/gaearon/redux-thunk
In Action:
import * as types from "./action-types";
import axios from "axios";
export const startAddPerson = person => {
return (dispatch) => {
return axios
.get(`https://599be4213a19ba0011949c7b.mockapi.io/cart/Cart`)
.then(res => {
dispatch(addPersons(res.data));
});
}
};
export const addPersons = personList => {
return {
type: types.ADD_PERSON,
personList
};
}
In PersonComponent:
class Person extends Component {
constructor(props){
super(props);
}
componentWillMount() {
this.props.dispatch(startAddPerson())
}
render() {
return (
<div>
<h1>Person List</h1>
</div>
);
}
}
export default Redux.connect()(Person);
You need two actions here: postPerson and addPerson.
postPerson will perform the API request and addPerson will update the store:
const addPerson = person => {
return {
type: types.ADD_PERSON,
person,
}
}
const postPerson = () => {
return (dispatch, getState) => {
return axios.get(`http://599be4213a19ba0011949c7b.mockapi.io/cart/Cart`)
.then(res => dispatch(addPerson(res.data)))
}
}
in your component, call postPerson()
You use the redux-thunk library which gives you access to the "getState" and "dispatch" methods. I see that that has been added by Chenxi to your question. Run your async operation first within your action and then call "dispatch" with your simple action action creator, which will return the simple object that redux is looking for.
Here is what your async action creator and your simple action creator(broken out into two action creators) will look like:
export const addPersonAsync = (person) => {
return (dispatch) => {
var response = [];
axios
.get(`http://599be4213a19ba0011949c7b.mockapi.io/cart/Cart`)
.then(res => {
response = res.data;
dispatch(addPerson(response));
});
};
};
export const addPerson = (response) => ({
type: types.ADD_PERSON,
response
});
From your component, you'll now call the "addPersonAsync" action creator.