React redux can not get the dispatch right - javascript

I am trying to make a call that changes redux state but i am having problems with dispatching the action. I am sure all imports are correct. I think the main problem is in mapStateToProps but just cant seem to find it.
Call
onClick={() => this.props.ethereum}
mapStateToProps and other...
const mapStateToProps = state => {
return({
depositMenu: state.depositMenu
})
}
const mapDispatchToProps = dispatch => {
return ( {
visa: () => dispatch(visa()),
bitcoin: () => dispatch(bitcoin()),
ethereum: () => dispatch(ethereum())
})
}
export default connect(
mapStateToProps,mapDispatchToProps
)(Deposit)
Actions
export const visa= () => {
return {
type: 'VISA'
}
}
export const bitcoin = () => {
return {
type: 'BITCOIN'
}
}
export const ethereum = () => {
return {
type: 'ETHEREUM'
}
}
Reducer
const MainPageDeposit = (state = 'visa', action) => {
switch (action.type) {
case 'VISA':
return state = 'visa';
case 'ETHEREUM':
return state = 'ethereum';
case 'BITCOIN':
return state = 'bitcoin';
default:
return state;
}
}
export default MainPageDeposit;
And combine reducers
import MainPageDeposit from './MainPageDeposit';
import { combineReducers } from 'redux';
const allReducers = combineReducers({
depositMenu: MainPageDeposit,
})
export default allReducers;

I think you should change onClick={() => this.props.ethereum} to onClick={this.props.ethereum}

Related

How to properly update redux state with a boolean value?

I have a React app with a currency unit switch. I have a function to switch the unit and update redux so that every component that has called the unit will be re-rendered. The problem is the redux prop (storedCurrencyUnit) is UNDEFINED whenever I updated the value and call the update function to redux.
Switch component
import { compose } from 'recompose';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { updateCurrencyUnit } from '../../store/actions';
class FrontHeader extends Component {
handleCurrencyChange = (e) => {
const { updateCurrencyUnit, storedCurrencyUnit } = this.props;
updateCurrencyUnit(e.target.checked)
console.log("unit", storedCurrencyUnit) // this is UNDEFINED
this.setState({ aud: e.target.checked }, () => {
localStorage.setItem("currencyUnit", this.state.aud ? "AUD" : "USD")
})
}
render() {
return (
<Switch
checked={this.state.aud}
onChange={this.handleCurrencyChange}
color="secondary"
name="aud"
inputProps={{ 'aria-label': 'currencyUnit' }}
/>
)
}
}
const mapStateToProps = (state) => ({
storedCurrencyUnit: state.storedCurrencyUnit
})
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({
updateCurrencyUnit: updateCurrencyUnit,
}, dispatch);
}
export default compose(connect(mapStateToProps, mapDispatchToProps))(FrontHeader);
currencyReducer.js
const storedCurrencyUnit = (state = null, action) => {
switch (action.type) {
case 'UPDATE_CURRENCYUNIT':
return action.payload;
default:
return state;
}
}
export default storedCurrencyUnit;
actions.js
export const updateCurrencyUnit = (updatedCurrencyUnit) => {
return {
type: 'UPDATE_CURRENCYUNIT',
payload: updatedCurrencyUnit,
}
}
How can I solve this?
You need to dispatch the action using dispatcher. only that will maintain the promise and let know the redux store.
this.props.dispatch(updateCurrencyUnit("some value"));

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

React-Redux - TypeError: state.menu is not iterable

I am working on a React application and I am using Redux to store the state. I have the following code:
menu.reducer.js:
import { INCREASE_CATEGORY_RANK, DECREASE_CATEGORY_RANK } from './menu.types';
const INITIAL_STATE = []
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case INCREASE_CATEGORY_RANK: {
console.log("Printing state");
console.log(state);
const menuArray = [...state.menu];
menuArray.sort((a,b) => (a.rank > b.rank) ? 1 : -1);
return {
...state,
menu: menuArray
}
}
case DECREASE_CATEGORY_RANK: {
return state.map(category => {
if (category._id === action.payload._id && category.rank !== -1) {
const oldrank = category.rank;
return {
...category,
rank: oldrank - 1
}
} else {
return category;
}
})
}
default:
return state;
}
}
menu.types.js:
export const INCREASE_CATEGORY_RANK = "INCREASE_CATEGORY_RANK";
export const DECREASE_CATEGORY_RANK = "DECREASE_CATEGORY_RANK";
menu.actions.js:
import { apiUrl, apiConfig } from '../../util/api';
import { INCREASE_CATEGORY_RANK, DECREASE_CATEGORY_RANK } from './menu.types';
export const getMenu = () => async dispatch => {
const response = await fetch(`${apiUrl}/menu`);
if (response.ok) {
const menuData = await response.json();
dispatch({ type: GET_MENU, payload: menuData })
}
}
export const increaseCategoryRank = category => dispatch => {
dispatch({ type: INCREASE_CATEGORY_RANK, payload: category })
}
export const decreaseCategoryRank = category => dispatch => {
dispatch({ type: DECREASE_CATEGORY_RANK, payload: category })
}
category-arrows.component.jsx:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { increaseCategoryRank, decreaseCategoryRank } from '../../redux/menu/menu.actions';
import './category-arrows.styles.scss';
class CategoryArrows extends Component {
render() {
const { category, increaseCategoryRank, decreaseCategoryRank } = this.props;
return (
<div class="arrows-container">
<div class="up-arrow" onClick={() => this.props.increaseCategoryRank(category)}></div>
<div class="category-rank">
<p>{category.rank}</p>
</div>
<div class="down-arrow" onClick={() => this.props.decreaseCategoryRank(category)}></div>
</div>
)
}
}
export default connect(null, { increaseCategoryRank, decreaseCategoryRank } )(CategoryArrows);
For my Reducer function, the initial state is retrieved from a database. The Reducer code deals with the menu array, which is an array of objects:
I want to copy the menu array from the state in my Reducer function, so that I can sort it, and then reassign the sorted menu array to the state.
I have tried to console.log() the state to see what it is in my Reducer function. When I click on the up-arrow div in category-arrows.component.jsx, the INCREASE_CATEGORY_RANK action is dispatched. When I check the Console, I get the following:
However when I copy the menu array from the state in the INCREASE_CATEGORY_RANK case in my Reducer function, I get the following error:
I am not sure why I am getting the above error and how to resolve it. Any insights are appreciated.
It looks like the reducer expect an array not an object:
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case INCREASE_CATEGORY_RANK: {
return [...state].sort((a,b) => (a.rank > b.rank) ? 1 : -1);
}
}
}

Call fetch function in functional component React

I'v tried so many way to fetch data only once before rendering but have some issue:
1) I Can't call dispatch in componentDidMount because there is the rule that I can do it in Functional component only
2) If I try to call fetch function in the beginning of a Functional component it starts to rerender infinitely because fetch function calls every time and change a state in a redux store
3) I found a solution with useEffect but it generate exception "Invalid hook call" like in first point
How can I call fetch function only once in this component?
here is my component:
import React, { useEffect } from "react";
import { useParams as params} from "react-router-dom";
import { VolunteerCardList } from "./VolunteerCardList";
import { AnimalNeeds } from "./AnimalNeeds";
import { AppState } from "../reducers/rootReducer";
import { connect } from "react-redux";
import { Page404 } from "./404";
import { fetchAnimal } from "../actions/animalAction";
import { Dispatch } from "redux";
import { IAnimalCard } from "../interfaces/Interfaces";
const AnimalCard: React.FC<Props> = ({animal, loading, fetch}) => {
useEffect(() => {
fetch(); //invalid hook call????
}, [])
return (
<div className="container">
some html
</div>
)
}
interface RouteParams {
shelterid: string,
animalid: string,
}
interface mapStateToPropsType {
animal: IAnimalCard,
loading : boolean
}
const mapStateToProps = (state: AppState) : mapStateToPropsType=> {
return{
animal: state.animals.animal,
loading: state.app.loading
}
}
interface mapDispatchToPropsType {
fetch: () => void;
}
const mapDispatchToProps = (dispatch: Dispatch<any>) : mapDispatchToPropsType => ({
fetch : () => {
const route = params<RouteParams>();
dispatch(fetchAnimal(route.shelterid, route.animalid));
}
})
type Props = ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>;
export default connect(mapStateToProps, mapDispatchToProps as any)(AnimalCard);
this is my reducer:
export const animalReducer = (state: AnimalReducerType = initState, action: IAction) => {
switch (action.type) {
case AnimalTypes.FETCH_ANIMAL:
return {...state, animal: action.payload};
break;
default:
return state;
break;
}
this is action:
export interface IFetchAnimalAction {
type: AnimalTypes.FETCH_ANIMAL,
payload: IAnimalCard
}
export type IAction = IFetchAnimalAction;
export const fetchAnimal = (shelterId : string, animalId: string) => {
return async (dispatch: Dispatch) => {
const response = await fetch(`https://localhost:44300/api/animals/${animalId}`);
const json = await response.json();
dispatch<IFetchAnimalAction>({type: AnimalTypes.FETCH_ANIMAL, payload: json})
}
}
This runs as old lifecycle method componentDidMount:
useEffect(() => {
fetch(); //invalid hook call????
}, [])
I guess the behaviour you want to replicate is the one iterated by componentWillMount, which you cannot do by any of the standard hooks. My go-to solution for this is to let the acquire some loadingState, most explicitly as:
const AnimalCard: React.FC<Props> = ({animal, loading, fetch}) => {
const [isLoading, setIsLoading] = useState<boolean>(false);
useEffect(() => {
fetch().then(res => {
// Do whatever with res
setIsLoading(true);
}
}, [])
if(!isLoading){
return null
}
return (
<div className="container">
some html
</div>
)
}

mapStateToProps not updating when redux store updates

I'm working in a react native app with react redux integration. When I call dispatch from a service my store is getting updated but somehow my component is not re-rendering.
Is it wrong to call dispatch from a service file and not from mapDispatchToProps function.
store.js
import { memesReducer } from './memesReducer'
export default combineReducers({
memesReducer
});
export default configureStore = () => {
const store = createStore(rootReducer);
return store;
}
memesReducer.js
const initialState = { memeList: [] }
export const memesReducer = (state = initialState, action) => {
switch (action.type) {
case LOAD_MEMES: {
return { ...state,
memeList: action.data
}
}
default:
return state;
}
}
memeService.js
import configureStore from '../redux/store';
import { loadMemes } from '../redux/actions';
const store = configureStore();
export const getMemesList = () => {
axios('https://jsonplaceholder.typicode.com/albums')
.then(response => {=
store.dispatch(loadMemes(response.data))
})
.catch(error => { console.error('getMemesList : ', error); })
}
memeActions.js
export const loadMemes = memesListData => ({
type: LOAD_MEMES,
data: memesListData
});
MemeList.js
class MemeList extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
getMemesList()
}
render() {
const memeListData = this.props.memeList.map((meme) => <MemeCard meme={meme} />)
return (
<Container>
<Content>
<List>
{ memeListData }
</List>
</Content>
</Container>
)
}
}
const mapStateToProps = state => {
return {
memeList: state.memesReducer.memeList,
}
}
export default connect(mapStateToProps)(MemeList);
memeActions.js
export const getMemesList = () => dispatch => {
axios("https://jsonplaceholder.typicode.com/albums")
.then(response => dispatch(loadMemes(response.data)))
.catch(error => {
console.error("getMemesList : ", error);
});
};
const loadMemes = memesListData => ({
type: "LOAD_MEMES",
data: memesListData
});
memeReducer.js
case "LOAD_MEMES": {
return { ...state, memeList: action.data };
}
index.js
export default combineReducers({
memesReducer: memeReducer
});
memeList.js
class memeList extends Component {
componentDidMount() {
this.props.getMemesList();
}
render() {
console.log(this.props.memeList);
return <div>MemeList</div>;
}
}
const mapStateToProps = state => ({
memeList: state.memesReducer.memeList
});
export default connect(
mapStateToProps,
{ getMemesList }
)(memeList);
Yeah bro it wouldn't work. You should call dispatch in a Redux connected component.
What you can do is await or attach a .then to the Service Call and THEN call the dispatch after the await or inside the .then.
call your actions like this then only i will work.
componentDidMount() {
this.props.getMemesList()
}
for your more clarification check this official documentation react redux

Categories