I am having a wierd issue in react-redux , i am getting all the state instead of the state that i passed
this is my code:
Action.js
import socketIOClient from 'socket.io-client'
const DATA_URL = "LINK TO API";
export const GET_DATA ='GET_DATA';
export const LIVE_DATA = 'LIVE_DATA';
const parseData= arr => arr.reverse().map((i)=>([i[0],i[1],i[3],i[4],i[2],i[5]]))
export const getData = () => dispatch =>{
fetch(DATA_URL).then(res => res.json())
.then(data => dispatch({
type:GET_DATA,
payload:parseData(data)
}
))
}
export const getLive = () => dispatch => {
var checkTime=0;
const socket = socketIOClient('http://localhost:3001');
socket.on("candle",(res)=>{
if(checkTime <= res[0]){
checkTime = res[0];
dispatch({
type:LIVE_DATA,
payload:res
})
}
})
}
api.js
import {GET_DATA,LIVE_DATA} from '../actions/index';
const INITIAL_STATE = {all:[],live:[]}
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_DATA:
return Object.assign({},state,{all:action.payload})
case LIVE_DATA:
return Object.assign({},state,{live:action.payload})
default:
return state;
}
}
reducer.js
import { combineReducers } from 'redux';
import all from './api';
import live from './api';
const reducers = combineReducers({
candle:all,
livedata:live
});
export default reducers;
As you can see i am passing all to candle and live to livedata
But in my Reduxdevtools i can access everything from both candle and livedata as you can see in the screen shot
This is how i dispatch the action on my component
App.js
const mapStateToProps = state => ({
data: state.candle.all,
live: state.livedata.live
})
Can someone tell me what need to be changed so i could be able to access only
live in the livedata state and all only in the candle state
Thank you
This happened because you're accessing the same reducer but with different names. You should create separate reducer for each.
Like this:
candleReducer.js
import {GET_DATA} from '../actions';
const INITIAL_STATE = { all:[] }
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_DATA:
return Object.assign({},state,{all:action.payload})
default:
return state;
}
}
liveReducer.js
import {LIVE_DATA} from '../actions';
const INITIAL_STATE = { live:[] }
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case LIVE_DATA:
return Object.assign({},state,{ live:action.payload })
default:
return state;
}
}
and then import them into combine reducers:
import { combineReducers } from 'redux';
import all from './candleReducer';
import live from './liveReducer';
const reducers = combineReducers({
candle:all,
livedata:live
});
export default reducers;
You are using the same reducer function for two 'part' of the state.
In your case you are duplicating the same logic in two parts, so the same reducer is called and it react to the same actions and update two entries in the state with the same logic.
You should consider to write two separate reducer for candle and livedata, so each of them will react to a specific action and modify correct entry in the state.
But if candle and livedata are releated to the same domain you should consider to put in one reducer and of course in one section of state, so you will end up in this situation
const reducers = combineReducers({
apiData:liveAndCandleReducer,
});
In apiData you will have
{
all:[],
live: [],
}
It's totally up to you and your application logic.
Related
Language used : JS with REACT REDUX
Here is the context: I have a page where the user can see all the forms made.
At the initialization of my application, I dispatch 'get forms'. this dispatch allows me via axios to retrieve all the forms I have in db
useEffect(() => {
dispatch(getForms());
}, []);
i have an action and reducer file :
actions
import axios from "axios";
export const GET_ALL_FORMS = "GET_ALL_FORMS";
export const getForms = () => (dispatch) =>
axios
.get(`/api/form`)
.then((res) => {
dispatch({ type: GET_ALL_FORMS, payload: res.data });
})
.catch((err) => err);
reducer
import { GET_ALL_FORMS } from "../actions/forms.actions";
const initialState = [];
export default function allFormsReducer(state = initialState, action) {
switch (action.type) {
case GET_ALL_FORMS:
return action.payload;
default:
return state;
}
}
Then in my "folder" page I use 'useSelector' to retrieve all the forms and display them (working).
import React, { useState, useEffect } from "react";
import { useSelector } from "react-redux";
export const FolderNew = () => {
const formData = useSelector((state) => state.allFormsReducer);
But now, at the click of a button, I would like to sorted my forms by date
Normally I don't use redux, so I would just do an axios request in my folder page and then use 'useState' (allForms, setAllForms) with my res.data and refresh the state like this :
const descend = () => {
const sorted = [...allForms].sort((a, b) =>
b.createdAt.localeCompare(a.createdAt)
);
setAllForms(sorted);
};
But now, with redux i'm really lost.
What i'm actually trying : Change the reducer to sort here the state (not working for the moment)
You can create a dispatch action something like sortForms(), then write the logic in your reducer:
export default function allFormsReducer(state = initialState, action) {
switch (action.type) {
case 'SORT_ALL_FORMS': {
const sortedForms = ....
return sortedForms
}
}
}
As Redux documents state, you should put as much logic as possible in your reducers:
https://redux.js.org/style-guide/#put-as-much-logic-as-possible-in-reducers
I can't update my store values using action creator. All needed values I get from component normally, but just can't substitute the initial state values with them. I believe I made a stupid mistake or even misspelled something, but I spent a lot of time already, and the console in the browser still shows me the empty strings in that reducer's state.
let initialstate = {
login: "",
password: ""
}
const formReducer = (state = initialstate ,action) => {
switch (action.type) {
case 'SET-FORM-DATA': {
return {
...state,
login: action.login,
password: action.password
}
}
default: return state;
}
}
export const SetFormData = (login, password) => ({
type: 'SET-FORM-DATA', login, password
})
export default formReducer;
That formReducer is normally assigned in redux-store
import { applyMiddleware, combineReducers, createStore } from 'redux';
import authReducer from './auth-reducer';
import dialogsReducer from './dialogs-reducer';
import profileReducer from './profile-reducer';
import usersReducer from './users-reducer';
import thunkMiddleware from 'redux-thunk';
**import formReducer from './form-reducer';**
let reducers = combineReducers({
profilePage: profileReducer,
dialogPage: dialogsReducer,
usersPage: usersReducer,
auth: authReducer,
form: formReducer
})
let store = createStore(reducers, applyMiddleware(thunkMiddleware));
window.store = store;
export default store;
I call this action creator on submit of form like that:
const onSubmit = formData => {
props.SetFormData(formData.Login, formData.password)
}
And this call seems to be, luckily works alright
I am Brazilian and therefore I speak Portuguese, but I will use the translator to try to help you.
I didn't quite understand the problem, but to handle redux we must use dispatch. You are not using this, you are just passing redux values. Sorry if I got it wrong, but I use something like this:
import { createTypes } from "reduxsauce";
export const gradesTypes = createTypes(
`
SET_GRADES
RESET_STATE
`,
{ prefix: "GRADES/" }
);
const setGrades = (grades) => {
return (dispatch) => dispatch({ type: gradesTypes.SET_GRADES, grades });
};
const resetState = () => {
return (dispatch) => dispatch({ type: gradesTypes.RESET_STATE });
};
export const gradesActions = {
setGrades,
resetState,
};
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
The common cause for my issue when researching this is mutating the state and not returning a new object of the state which causes redux to not recognize a change. However, this is not and has never been an issue and i'm well aware of it. I'm returning a new object. In the logger which you can see in the attached image it displays the successful api call resolved and the nextState is updated but never rendered. Refreshing the page acts exactly the same even though i expected to possibly need to do so upon initial landing to root page.
Component:
import pokemonReducer from '../../reducers/pokemon_reducer';
import PokemonIndexItem from './pokemon_index_item';
import {Route} from 'react-router-dom';
import PokemonDetailContainer from './pokemon_detail_container';
class PokemonIndex extends React.Component {
componentDidMount() {
this.props.requestAllPokemon();
}
render() {
const pokemon = this.props.pokemon;
return (
<section className="pokedex">
<Route path='/pokemon/:pokemonID' component={PokemonDetailContainer} />
<ul>{pokemon && pokemon.map(poke => <li>{poke.name}{poke.id}</li>)}</ul>
</section>
);
}
}
export default PokemonIndex;
and the container:
import {connect} from 'react-redux';
import { selectAllPokemon } from '../../reducers/selectors';
import PokemonIndex from './pokemon_index';
import { requestAllPokemon } from '../../actions/pokemon_actions';
const mapStateToProps = state => ({
pokemon: selectAllPokemon(state)
});
const mapDispatchToProps = dispatch => ({
requestAllPokemon: () => dispatch(requestAllPokemon())
});
export default connect(mapStateToProps, mapDispatchToProps)(PokemonIndex);
the reducer:
import { RECEIVE_ALL_POKEMON, RECEIVE_SINGLE_POKEMON} from '../actions/pokemon_actions';
const pokemonReducer = (initialState = {}, action) => {
Object.freeze(initialState);
switch(action.type) {
case RECEIVE_ALL_POKEMON:
return Object.assign({}, initialState, action.pokemon);
case RECEIVE_SINGLE_POKEMON:
let poke = action.payload.pokemon
return Object.assign({}, initialState, {[poke.id]: poke})
default:
return initialState;
}
};
export default pokemonReducer;
secondary reducer:
import { combineReducers } from 'redux';
import pokemonReducer from './pokemon_reducer'
const entitiesReducer = combineReducers({
pokemon: pokemonReducer,
});
export default entitiesReducer;
rootreducer:
import {combineReducers} from 'redux';
import entitiesReducer from './entities_reducer';
const rootReducer = combineReducers({
entities: entitiesReducer
});
export default rootReducer;
as requested here is the selectors defined in reducers folder
export const selectAllPokemon = (state) => {
Object.values(state.entities.pokemon);
};
export const selectSinglePokemon = (state) => {
Object.values(state.entities.pokemon)
};
and here is the actions created:
export const RECEIVE_ALL_POKEMON = "RECEIVE_ALL_POKEMON";
export const RECEIVE_SINGLE_POKEMON = "RECEIVE_SINGLE_POKEMON";
import * as APIUtil from '../util/api_util';
export const receiveAllPokemon = (pokemon) => (
{
type: RECEIVE_ALL_POKEMON,
pokemon
}
);
export const requestAllPokemon = () => (dispatch) => {
APIUtil.fetchAllPokemon()
.then(
pokemon =>
{ dispatch(receiveAllPokemon(pokemon));}
);
};
export const receiveSinglePokemon = data => (
{
type: RECEIVE_SINGLE_POKEMON,
data
}
);
export const requestSinglePokemon = id => (dispatch) => {
APIUtil.fetchSinglePokemon(id)
.then(pokemon => {dispatch(receiveSinglePokemon(pokemon));
return pokemon;});
};
nextstate showing in console
As you stated in your question, your redux state is getting properly set but your new state is never being rendered and I think this has to do with your selector. It looks to me that you forgot to return your computed state.
export const selectAllPokemon = (state) => {
Object.values(state.entities.pokemon);
};
// will return undefined
For returning your state you have two options:
Explicit return
export const selectAllPokemon = (state) => {
return Object.values(state.entities.pokemon);
};
Implicit return
export const selectAllPokemon = (state) => (
Object.values(state.entities.pokemon);
);
I refer to this article or look at the examples I created in playground to get a better unstanding of implicit and explicit return in arrow functions.
my Redux fetch is returning empty..
It does not break but it just returns me empty object.
Here is the code for my action (newsActions.js):
import axios from 'axios';
import kickstarterData from '../server/kickstarter-october.json';
export const FETCH_KICKSTARTER = 'FETCH_KICKSTARTER';
export function fetchKickstarter() {
return {
type: FETCH_KICKSTARTER,
payload: {
data: kickstarterData
}
};
}
Here is my Reducer:
import { FETCH_KICKSTARTER } from '../actions/kickstarterActions';
export default function(state = [], action) {
switch (action.type) {
case FETCH_KICKSTARTER:
debugger;
return [action.payload.data, ...state];
}
return state;
};
https://stackoverflow.com/questions/ask#
Here is my index.js that combines all the reducers:
import { combineReducers } from 'redux';
import NewsReducer from './reducer_news';
import KickstarterReducer from './reducer_kickstarter';
const rootReducer = combineReducers({
news: NewsReducer,
kickstarters: KickstarterReducer
});
export default rootReducer;
Finally, inside my app.js I have the following code:
const mapStateToProps = (state) => ({
news: state.news,
kickstarters: state.kickstarters
});
export default connect(mapStateToProps, {...newsActions, ...kickstarterActions})(App);
Could anyone tell me why this is breaking?
Also, could anyone suggest me a better/cleaner way of writing these codes?
Thank you
I have a hunch that in your reducer
return [action.payload.data, ...state];
should be
return [...action.payload.data, ...state];
Frankly it should be just
return [...action.payload.data ];
Since i don't have any idea about your biz logic, but later seems more correct to me (why do you need to merge it with old state).
you need to spread the action.payload.data in the state.