Redux action not firing - no errors - javascript

I'm trying to call a simple action on click which fires a dispatch action. I can't seem to get a result or even indiciation that it's firing.
I'm trying to dispatch on click in a component. I've also tried putting a console.log in the action to see if it even gets fired but it doesn't. Redux dev tools also doesn't suggest it even gets fired on click.
onClick={() => {
setAQIType(name);
}}
Action:
import { SET_AQITYPE } from "./types";
export const setAQIType = (AQIType) => dispatch => {
dispatch({
type: SET_AQITYPE,
payload: { AQIType }
});
};
Reducer:
import { SET_AQITYPE } from '../actions/types';
const initialState = {
aqiType: 'DEFAULT',
loading: false,
};
export default function(state = initialState, action){
const { type, payload } = action;
switch(type){
case SET_AQITYPE:
return [...state, payload];
default:
return state;
}
}
Types:
export const SET_AQITYPE = 'SET_AQITYPE';

Three errors,
In reducer: Your state is an object and not a list.
In reducer: Assign payload to aqiType key
In dispatch: Payload is a string and not an object.
To fix:
export const setAQIType = (AQIType) => dispatch => {
dispatch({
type: SET_AQITYPE,
payload: AQIType // (3) pass as string
});
};
// In reducer
case SET_AQITYPE:
return { // (1) object
...state,
aqiType: payload // (2) specify aqiType key
};
This assumes that you've checked the basic example with connect() and mapDispatchToProps.

Most likely you missed to connect the component with the redux store, which means there is no dispatch function passed to your action.
https://react-redux.js.org/using-react-redux/connect-mapdispatch
Cheers

Try to return inside action function as below:
import { SET_AQITYPE } from "./types";
export const setAQIType = (AQIType) => dispatch => {
return dispatch({
type: SET_AQITYPE,
payload: { AQIType }
});
};

Related

How do I reference a variable in redux store from a component index.js (react.js)

I'm a newbie in redux and react.js,
I am trying to make a button disappear on a component in react.js by putting an if condition on the state variable (articlesTable/index.js), which is connected to the redux library function on another file (actions/actionArticles.js), when a button on articlesTable/index.js is clicked, the component is connected with actions/actionArticles.js and dispatch a function in actions/actionArticles.js, which is called loadMoreData().
The function I am trying to configure the state in redux is,
in articlesActions.js
export const loadMoreArticles = () => async (dispatch, getState) => {
const lastArticleKey = Object.keys(getState().articlesMap).pop();
const lastArticle = getState().articlesMap[lastArticleKey];
console.log("articleMap", getState().articlesMap);
console.log("Last article", lastArticleKey, lastArticle);
let filteredArticles = {};
const uid = getState().auth.uid;
const userLevel = getState().profile.userLevel;
} else {
const filteredArticlesArray = [];
var lastArticleReached = false;
...
var lastArticleInArray = filteredArticlesArray[filteredArticlesArray.length-1];
if (lastArticleInArray[0]===lastArticleKey) {
console.log("Bingo, last article reached!");
lastArticleReached = true;
}
else if (lastArticleInArray[0]!== lastArticleKey)
{
console.log("Not last article");
lastArticleReached = false;
}
filteredArticles = Object.fromEntries(filteredArticlesArray.reverse());
}
dispatch({type: LAST_ARTICLE_REACHED, payload: lastArticleReached})
...
};
I dispatch this function with
dispatch({ type: LOAD_MORE_ARTICLES, payload: filteredArticles });
in the code snippet above
The root reducer looks like this,
reducers/index.js
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import articlesStatusReducer from './articlesStatusReducer';
const rootReducer = combineReducers({
...
articlesStatus: articlesStatusReducer,
form: formReducer,
...
});
export default rootReducer;
In articleStatusReducer,
import {LAST_ARTICLE_REACHED} from "../actions/types";
export default function(state = {}, action) {
switch(action.type) {
case(LAST_ARTICLE_REACHED):
console.log(action.payload);
return action.payload;
default:
return state;
}
}
In the articlesTable/index.js, I connect like this
const mapStateToProps = (state) => {
return {
articlesMap: state.articlesMap,
appStatus: state.appStatus,
profile: state.profile,
lastArticleReached: state.articlesStatus,
}
};
const mapDispatchToProps = (dispatch) => {
return {
getArticlesWithData: () => dispatch(getArticlesWithData()),
loadMore: () => dispatch(loadMoreArticles())
}
};
export default compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
)(ArticlesTable)
For some reason, articleStatus isn't recognised and when I do
console.log(this.props.articleStatus)
state.articleStatus is undefined
How can I reference state.articleStatus which should be boolean ?
Edit:
For some reason when I put it in a conditional JSX brackets in the render method, it prints out undefined
render () => {
{
console.log(this.props.lastArticleReached),
!this.props.lastArticleReached
: <Button> </Button>
?
<div><div>
}
}``
In function mapStateToProps, you should map state.articleStatus to a props.
somethings like this:
const mapStateToProps = (state) => {
return {
articlesMap: state.articlesMap,
appStatus: state.appStatus,
profile: state.profile,
lastArticleReached: state.articlesStatus,
articleStatus: state.articleStatus
}
};
So this.props.articleStatus will works . :)
The problem is in your reducer. Each case of your reducer must return the state but in your case, your return action.payload.
try something like this.
case(LAST_ARTICLE_REACHED):
console.log(action.payload);
return {...state, articleStatus: action.payload};
like this, articlesStatus became an object with one props, articleStatus, your boolean.
I tried another name for the props but with similar method as Thomas Caillard,
Reducer.js
case(REACH_LAST_ARTICLE):
return {...state, lastArticleReached: action.payload}
in component index.js
const mapStateToProps = (state) => {
return {
...
lastArticleReached: state.articlesMap.lastArticleReached
...
}
};
Thanks for all the helps so far

Action firing, but reducer is not

Been struggling with this one for a while any help appreciated. With the below code it looks like the action fires successfully, but the reducer does not. No values are updated in redux store for rooms, but can confirm redux store is setup correctly and works fine for all my user actions and reducers.
I'm firing a function within a component getRoom('someId), passing along an id which then does a fetch and returns the result. Upon getting that result I'm dispatching a redux action saveRoom(res).
getRoom('someId')
.then(res => {
saveRoom(res);
})
.catch(e => console.log('Error', e));
Get room is just a fetch:
// Get room by id
export const getRoom = (roomId) => {
return fetch(`${API}/room/read/${roomId}`, {
method: "GET"
})
.then(response => {
return response.json();
})
.catch(err => console.log('Error', err))
};
Save room is a simple action. I can see it is being called and fired correctly in my console log.
import {SAVE_ROOM} from "./types";
export const saveRoom = (data) => {
console.log('Saving Room.', data);
return {
type: SAVE_ROOM,
payload: data
}
}
However, it doesn't seem like my reducer is firing at all. I have a console log to check but nothing returns in the console, and the redux store does not update.
import { SAVE_ROOM } from "../actions/types";
const initialState = { loading: true };
export default (state = initialState, action) => {
console.log('Save room reducer. Payload is: ', action.payload)
switch (action.type) {
case SAVE_ROOM:
return {
...state,
loading: false,
room: action.payload
};
default:
return state;
}
}
I have also ensured that the reducers are combined and can confirm they're present using redux tools
import { combineReducers } from "redux";
import userReducer from "./userReducer";
import roomReducer from "./roomReducer ";
export default combineReducers({
user: userReducer,
room: roomReducer
});
const dispatch = useDispatch()
and then
dispatch(saveRoom(res))

Why list doesn't appear on the page? What are the errors in my React-Redux (Api) application? And how to fix them?

First, I made a small application on the React.js. Using the fetch method, I take the API
And these are the main files of my application:
Index.js:(action)
export const SHOW_AIRPLANES = "SHOW_AIRPLANES";
export function showAirplanes() {
return (dispatch, getState) => {
fetch("https://api.iev.aero/api/flights/25-08-2019").then(response => {
dispatch({ type: SHOW_AIRPLANES, payload: response.data });
});
};
}
airplanes.js:(reducer)
import { SHOW_AIRPLANES } from '../actions'
const initialState = {
list: []
}
export function showAirplanes(state = initialState, action) {
switch (action.type) {
case SHOW_AIRPLANES:
return Object.assign({}, state, {list: action.payload})
default:
return state
}
}
index.js(reducer):
import { combineReducers } from "redux";
import { showAirplanes } from "./airplanes";
const rootReducer = combineReducers({
user: showAirplanes
});
export default rootReducer;
First, you should use the createStore function like so:
const initialData = {}; // whatever you want as initial data
const store = createStore(reducers, initialData, applyMiddleware(thunk));
Then pass it to your provider
<Provider store={store}>
{...}
</Provider
next, when you map your reducers inside the combineReducers function, each key in this object represents a piece of your state. So when you do user: showAirplanes it means that you intend to use it in the mapStateToProps with state.user.list so I think you meant to call it airplane: showAirplanes.
Then, your reducer name is not informative enough, I would suggest to change it to airplanesReducer.
Next issue, the call to fetch returns a response that has JSON that must be resolved.
Change this:
fetch("https://api.iev.aero/api/flights/25-08-2019").then(response => {
dispatch({ type: SHOW_AIRPLANES, payload: response.data });
});
To this:
fetch("https://api.iev.aero/api/flights/25-08-2019")
.then(res => res.json())
.then(response => {
dispatch({ type: SHOW_AIRPLANES, payload: response.body.departure });
});
Note that I've changed the value that you need to resolve from the response as well.
Inside your App.js component you need to create a constructor and bind the renderAirplaneList function to this
// Inside the App class
constructor(props) {
super(props);
this.renderAirplaneList = this.renderAirplaneList.bind(this);
}
And finally (I hope I didn't miss anything else), you map your state in the App.js component to { airplanes: state.airplanes.list} so the name of the prop you expect inside your component is props.airplanes.
renderAirplaneList() {
if (!this.props.airplanes.length) {
return null;
}
const arr = this.props.airplanes || [];
return arr.map(airplane => {
return (
<tr key={airplane.id}>
<td>{airplane.ID}</td>
<td>{airplane.term}</td>
<td>{airplane.actual}</td>
<td>{airplane["airportToID.city_en"]}</td>
</tr>
);
});
}
Make sure you go over the documentation of React and Redux, they have all the information you need.
Good luck.
aren't you suppose to send some parameters to this call?
this.props.showAirplanes()
it seems that it has 2 parameters: state and action, although state seems to have already it's default value

React Redux API call, data not making it back to component

In the last couple of days I have been working on my Redux api call. I am actually having a problem getting the data back to the view component. Currently I'm able to see the data in the in the action generator, so I know at least I'm able to get it. However, nothing is showing in the view. I imagine it may have something to do with when it's loading. This is why I tried to load it when the component is rendering.
https://djangoandreact.herokuapp.com/user/1 is what is not loading.
codesandbox: https://codesandbox.io/s/zlor60q3jm?from-embed
Should be able to go to /user/1 at the end similar to going to /1 brings up an article(Tough Hope)
Heres the view component:
import React from "react";
import { connect } from "react-redux";
import { fetchUser } from "../store/actions/userActions";
class UserDetailView extends React.Component {
componentDidMount() {
const userID = this.props.match.params.userID;
fetchUser(userID); //fixed
}
render() {
const { user } = this.props.user;
console.log(user);
return (
<div>
<h3>{user.username}</h3>
</div>
);
}
}
const mapStateToProps = state => ({
user: state.user
});
const mapDispatchToProps = (dispatch, ownProps) => ({
fetchUser: dispatch(fetchUser(ownProps.match.params.userID))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserDetailView);
Action generator
import axios from "axios";
import { thunk } from "react-redux";
export function fetchUser(userID) {
console.log(userID);
return dispatch => {
return axios.get(`/api/user/${userID}`).then(res => {
dispatch(fetchUserSuccess(res.data));
console.log(res.data); // loads data
});
};
}
// Handle HTTP errors since fetch won't.
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
export const FETCH_USER_BEGIN = "FETCH_USER_BEGIN";
export const FETCH_USER_SUCCESS = "FETCH_USER_SUCCESS";
export const FETCH_USER_FAILURE = "FETCH_USER_FAILURE";
export const fetchUserBegin = () => ({
type: FETCH_USER_BEGIN
});
export const fetchUserSuccess = user => ({
type: FETCH_USER_SUCCESS,
payload: { user }
});
export const fetchUserFailure = error => ({
type: FETCH_USER_FAILURE,
payload: { error }
});
Reducers(which are probably fine):
import {
FETCH_USER_BEGIN,
FETCH_USER_SUCCESS,
FETCH_USER_FAILURE
} from "../actions/actionTypes";
const initialState = {
user: {},
loading: false,
error: null
};
export default function userReducer(state = initialState, action) {
switch (action.type) {
case FETCH_USER_BEGIN:
return {
...state,
loading: true,
error: null
};
case FETCH_USER_SUCCESS:
return {
...state,
loading: false,
user: action.payload.user
};
case FETCH_USER_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
user: {}
};
default:
return state;
}
}
folks. I found it.
case FETCH_USER_SUCCESS:
return {
...state,
loading: false,
user: action.payload.user
};
user is supposed to be user:action.payload
Also, the user action was supposed to be
export const fetchUserSuccess = user => ({
type: FETCH_USER_SUCCESS,
payload: user
})
WOOOOW. But, honestly, I learned so much about Redux in the last two sleepless nights, it was worth the pain. Really was. Now, instead of copy pasta, I know what an action generator is and does, and reducer (obvi)

Accessing a part of reducer state from one reducer within another reducer

I do not know how to access a boolean isLoading flag from reducerForm.js reducer in reducerRegister.js. I have used combineReducers() and I use isLoading to disable a button during form submit.
It's initial state is false, after clicking submit, it changes to true. After the form submission is successful, isLoading is reset to false again. Below is the relevant code for this issue:
actionRegister.js
let _registerUserFailure = (payload) => {
return {
type: types.SAVE_USER_FAILURE,
payload
};
};
let _registerUserSuccess = (payload) => {
return {
type: types.SAVE_USER_SUCCESS,
payload,
is_Active: 0,
isLoading:true
};
};
let _hideNotification = (payload) => {
return {
type: types.HIDE_NOTIFICATION,
payload: ''
};
};
// asynchronous helpers
export function registerUser({ // use redux-thunk for asynchronous dispatch
timezone,
password,
passwordConfirmation,
email,
name
}) {
return dispatch => {
axios.all([axios.post('/auth/signup', {
timezone,
password,
passwordConfirmation,
email,
name,
is_Active: 0
})
// axios.post('/send', {email})
])
.then(axios.spread(res => {
dispatch(_registerUserSuccess(res.data.message));
dispatch(formReset());
setTimeout(() => {
dispatch(_hideNotification(res.data.message));
}, 10000);
}))
.catch(res => {
// BE validation and passport error message
dispatch(_registerUserFailure(res.data.message));
setTimeout(() => {
dispatch(_hideNotification(res.data.message));
}, 10000);
});
};
}
actionForm.js
export function formUpdate(name, value) {
return {
type: types.FORM_UPDATE_VALUE,
name, //shorthand from name:name introduced in ES2016
value
};
}
export function formReset() {
return {
type: types.FORM_RESET
};
}
reducerRegister.js
const INITIAL_STATE = {
error:{},
is_Active:false,
isLoading:false
};
const reducerSignup = (state = INITIAL_STATE , action) => {
switch(action.type) {
case types.SAVE_USER_SUCCESS:
return { ...state, is_Active:false, isLoading: true, error: { register: action.payload }};
case types.SAVE_USER_FAILURE:
return { ...state, error: { register: action.payload }};
case types.HIDE_NOTIFICATION:
return { ...state , error:{} };
}
return state;
};
export default reducerSignup;
reducerForm.js
const INITIAL_STATE = {
values: {}
};
const reducerUpdate = (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.FORM_UPDATE_VALUE:
return Object.assign({}, state, {
values: Object.assign({}, state.values, {
[action.name]: action.value,
})
});
case types.FORM_RESET:
return INITIAL_STATE;
// here I need isLoading value from reducerRegister.js
}
return state;
};
export default reducerUpdate;
reducerCombined.js
import { combineReducers } from 'redux';
import reducerRegister from './reducerRegister';
import reducerLogin from './reducerLogin';
import reducerForm from './reducerForm';
const rootReducer = combineReducers({
signup:reducerRegister,
signin: reducerLogin,
form: reducerForm
});
export default rootReducer;
This is where I use isLoading:
let isLoading = this.props.isLoading;
<FormGroup>
<Col smOffset={4} sm={8}>
<Button type="submit" disabled={isLoading}
onClick={!isLoading ? isLoading : null}
>
{ isLoading ? 'Creating...' : 'Create New Account'}
</Button>
</Col>
</FormGroup>
Mapping state to props within the same component
function mapStateToProps(state) {
return {
errorMessage: state.signup.error,
isLoading: state.signup.isLoading,
values: state.form.values
};
}
This is covered in the Redux FAQ at https://redux.js.org/faq/reducers#how-do-i-share-state-between-two-reducers-do-i-have-to-use-combinereducers:
Many users later want to try to share data between two reducers, but find that combineReducers does not allow them to do so. There are several approaches that can be used:
If a reducer needs to know data from another slice of state, the state tree shape may need to be reorganized so that a single reducer is handling more of the data.
You may need to write some custom functions for handling some of these actions. This may require replacing combineReducers with your own top-level reducer function. You can also use a utility such as reduce-reducers to run combineReducers to handle most actions, but also run a more specialized reducer for specific actions that cross state slices.
Async action creators such as redux-thunk have access to the entire state through getState(). An action creator can retrieve additional data from the state and put it in an action, so that each reducer has enough information to update its own state slice.
A reducer cannot access another reducer's state, but if you're using redux-thunk you can do so from within an action creator. As an example, you can define an action creator like this:
export const someAction = () =>
(dispatch, getState) => {
const someVal = getState().someReducer.someVal;
dispatch({ type: types.SOME_ACTION, valFromOtherReducer: someVal });
};
React Redux works on unidirectional data flow.
Action ---> Reducer /store ---> Reducer
Reducer works on small subset of store, you can not access store inside reducer which is not part of Reducer. you can either need to fire new action from the component based on reducer state return.

Categories