Redux store not updating by action - reactjs

I have setup a reducer in redux to to manage state. I am calling that reducer through an action creator. The action is dispatching (coz I hava subscribed to the store and put a console.log there)
This is what the reducer looks like
import C from "../constants"
import { combineReducers } from 'redux'
export const wishlist = (state = [], action) => {
switch (action.type) {
case C.REMOVE_PRODUCT_FROM_WISHLIST:
return state.filter(product => product.ProductName !== action.payload.ProductName)
default:
return state
}
}
export default combineReducers({
wishlist
})
Like I said, I have hooked it up with an action creator which I know is firing (again, coz the store is subscribing)
This is what the action creator looks like
import C from './constants'
export const removeProductFromWishlist = function(ProductName) {
return {
type: C.REMOVE_PRODUCT_FROM_WISHLIST,
payload: ProductName
}
}
Is there something I am missing here?
for reference, here is the initialState.json
{
"wishlist": [
],
}
Please let me know what am I doing wrong here?

Related

Where does the root reducer come from in this redux react example and how does dispatch knows which reducer to go?

Trying to go through redux react tutorial and I am wondering where rootReducer come from
import { createStore } from "redux";
import rootReducer from "./reducers";
export default createStore(rootReducer);
Does combineReducers auto export rootReducer?
https://codesandbox.io/s/9on71rvnyo?file=/src/redux/store.js
Second question in
/components/AddTodo.js
handleAddTodo = () => {
this.props.addTodo(this.state.input);
this.setState({ input: "" });
};
handleAddTodo calls the addTodo action
/redux/actions.js
export const addTodo = content => ({
type: ADD_TODO,
payload: {
id: ++nextTodoId,
content
}
});
Which just returns an object
But what connects the addTodo to the reducer itself in /reducers/todos.js?
export default function(state = initialState, action) {
switch (action.type) {
case ADD_TODO: {
.....
Does combineReducers auto export rootReducer?
Yes it does
export default combineReducers({ todos, visibilityFilter });
is enough
What connects the addTodo to the reducer itself in /reducers/todos.js?
The dispatch function, when you call this:
this.props.addTodo(this.state.input);
under the hood you're actually running something like this:
dispatch(addTodo(this.state.input))
This is the pseudo code for combineReducers:
function combineReducers({ reducer1, reducer2 }) {
return (state, action) => {
if (action.type === 'reducer1') return reducer1(state.reducer1, action);
if (action.type === 'reducer2') return reducer2(state.reducer2, action);
}
}
It's not entirely correct, esp. the returning part, but hopefully it gives you an idea.
Basically, what it does is to create one big reducer that works as if both reducers are combined into one. A reducer is, as you may already know, one function that accepts the previous state and the action, and returns the next state.
Second question: connect does the trick. Once you connect your addTodo prop to the store, the store which already is connected to the reducer, will call the reducer (again, ONE BIG function) with the (prevState, action) value. action will be the return value of addTodo action generator method.
And the reducer does the rest, i.e. checks if the matching action type (in this case, ADD_TODO) exists and does what it does.

What is the recommended way of accessing a variable from a different reducer?

What is the recommended way of accessing a variable from a different reducer?
import { createStore, combineReducers } from 'redux';
import mainReducer from './reducers/main';
import configReducer from './reducers/config';
const rootReducer = combineReducers({
main:mainReducer,
config:configReducer
});
const store = createStore(rootReducer);
export default store;
I have two different reducer and I have an action inside mainReducer where I want to access a variable inside configReducer. What is the proper way to do this with Redux?
export default function (state, action) {
switch (action.type) {
case "UPDATE_ACTIVE":
//need to check variable options inside configReducer
default:
return state;
}
}
Basically a reducer just returns a slice of the global state i.e store.
If you are using a middleware like thunk, you can share the data like so:
export function updateActive(params) {
return (dispatch, getState) => {
const { config } = getState(); // the part you want to access.
dispatch({
type: UPDATE_ACTIVE,
options: config.options,
params,
});
};
}
So you get all the data in the action itself and then dispatch with the ACTION_TYPE.
You can read more on sharing state between reducers on :
https://redux.js.org/faq/reducers/
https://redux.js.org/api/store#getstate

Redux state gets overwritten when calling 2 API using useEffect

I have 2 actions that calls different API. I dispatch these actions in a useEffect.
I have 2 reducers files, One for each, to store the data received from the API.
So, Basically I should be able to access both the data individually using useState.
But the secondly called API's data is overwriting the data of the first API. I don't understand how, because they are not even on the same file or even related.
Component
const items = useSelector((state) => state.lostchambers.items);
const lostChambersItems = useSelector((state) => state.sharklostdolsea.itemsLostChamber);
useEffect(() => {
dispatch(fetchingLostChambers());
dispatch(fetchSharkLostDolSea());
}, [dispatch]);
The Action for both the files looks like this I'm only posting here for one file as its the same code
import { FETCH_POSTS } from "./type";
import axios from "../../utils/Request";
export const fetchingLostChambers = () => async (dispatch) => {
const response = await axios.get("API");
const { data = false, status } = response;
if (status === 200) {
if (data) {
dispatch({
type: FETCH_POSTS,
items: data.acf,
});
}
}
};
The Reducer for both the actions looks like this but I'm only posting here for one file as its the same code
import { FETCH_POSTS } from "../actions/lostchambers/type";
const initialState = {
items: [],
};
export default (state = initialState, action) => {
switch (action.type) {
case FETCH_POSTS:
return {
...state,
...action,
};
default:
return state;
}
};
Combined Reducer
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import rootReducers from "./reducers";
const initialState = {};
const middleware = [thunk];
const store = createStore(rootReducers, initialState, applyMiddleware(...middleware));
export default store;
RootReducer
import { combineReducers } from "redux";
import venues from "./venues";
import lostchambers from "./lostchambers";
import sharklostdolsea from "./sharklostdolsea";
export default combineReducers({
venues,
lostchambers,
sharklostdolsea,
});
Am I missing something here? I just can't figure out the issue ,I'v been trying four hours now.
The main issue I see here is that you are using the same type constant for both actions and reducers.
The way redux works is that it will pass the actions through all of the reducers that are combined together and will run whatever state changes the reducer says happens. That's why when you set up reducers you need the base case to return state if nothing matches.
By using the same type in the actions, both reducers will see both actions that were dispatched and perform the change. So a race condition occurs and the last one that is returned shows in both parts of state.
You should be able to fix this by just changing the action and reducer type constant for one/both of them.

Loading Data from MongoDB into Redux Store fetching from my node API

I am working on my very first react app, and I have successfully setup my Node API and MongoDB, and am now trying to integrate redux into my application. I will try to share the relevant code snippets here.
First, in my node API, I have a model mlb_ids_logos_colors.js with some baseball data. My react app is currently getting this data using the following fetch using an async function:
export async function get_mlb_ids_logos_colors() {
return fetch('/mlb/mlb_ids_logos_colors')
.then(resp => {
return resp.json();
})
}
I would like to get the data from this endpoint into my redux store, and then from the redux store into the various components that will use it, but I am a bit stuck. As far as redux, I have the following files:
reducers/index.js (my rootReducer)
import { combineReducers } from 'redux';
import mlbIdsLogosColorsReducer from './mlb-ids-logos-colors-reducer';
export default combineReducers({
mlbIdsLogosColorsReducer
})
reducers/mlb-ids-logos-colors-reducer.js
export default function reducer (state={
mlbIdsLogosColorsData: [],
}, action) {
switch (action.type) {
case "FETCH_COLORS": {
return {...state, mlbIdsLogosColorsData }
}
}
return state;
}
actions/mlb-ids-logos-colors-action.js
export const FETCH_COLORS = "FETCH_COLORS";
export function fetchMlbIdsLogosColors() {
return function(dispatch) {
dispatch({type: "FETCH_COLORS"});
fetch('/mlb/mlb_ids_logos_colors')
.then(resp => {
return resp.json();
})
}
}
lastly, I setup my store in **store.js* as follows, and import this into my apps main index.js file:
store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
Any help with this is appreciated. For reference, I am receiving the following error when launching the app:
./src/reducers/mlb-ids-logos-colors-reducer.js
Line 7: 'mlbIdsLogosColorsData' is not defined no-undef
I'm aware this is a quite-obvious error message, but admittidly I'm not sure where I should be defining mlbIdsLogosColorsData.
Thanks in advance!
EDIT: I don't have to make any changes ever to the data at my /mlb/mlb_ids_logos_colors endpoint. I just want to get this data into the redux store, and then from the redux store into the components. I know to use mapStateToProps() and connect() in my components to get the data into the components.
EDIT2: I HAVE DIFFERENT NAMES FOR THE ACTION! let me fix that, and see if that resolves the issue!
I'm in a hurry sorry if I misleading you but roughly you are dispatching an action without data. You should use something like that in your action creator:
export function fetchMlbIdsLogosColors() {
return function(dispatch) {
fetch('/mlb/mlb_ids_logos_colors')
.then(resp => {
return resp.json()
.then( json => dispatch({type: "FETCH_COLORS", json}));
})
}
}
and then use this payload (json) in your reducer like that:
export default function reducer (state={
mlbIdsLogosColorsData: [],
}, action) {
switch (action.type) {
case "FETCH_COLORS": {
return {...state, mlbIdsLogosColorsData: action.json }
}
}
return state;
}
Again, this is a rough suggestion. I did not check your whole code. But you are getting undefined error since there is not a variable named mlbIdsLogosColorsData right now.

Redux Reducer that simply returns an action's value and not a state?

I have a Container, an actionsCreator, and a reducer. In the below code, what's enabling the Reducer to return action.text instead of an updated state object? I thought reducers had to always return states.
HelloWorldContainer.jsx
import { connect } from 'react-redux';
import HelloWorld from '../components/HelloWorld';
import * as actionCreators from '../actions/helloWorldActionCreators';
const mapStateToProps = (state) => ({ name: state.name });
export default connect(mapStateToProps, actionCreators)(HelloWorld);
helloWorldActionCreators.jsx
import { HELLO_WORLD_NAME_UPDATE } from '../constants/helloWorldConstants';
export const updateName = (text) => ({
type: HELLO_WORLD_NAME_UPDATE,
text,
});
helloWorldReducer.jsx
import { combineReducers } from 'redux';
import { HELLO_WORLD_NAME_UPDATE } from '../constants/helloWorldConstants';
const name = (state = '', action) => {
switch (action.type) {
case HELLO_WORLD_NAME_UPDATE:
return action.text
default:
return state;
}
};
const mainReducer = combineReducers({ name });
export default mainReducer;
(Code source: React on Rails).
The name is just a slice of state. And action.text is the updated state.
After combineReducers({ name }), the state tree looks like:
{
name: '..'
}
Besides, redux doesn't limit you that you can only use object as your state. If you pass name to createStore() directly without combineReducers, your entire state will become a plain string.
I thought reducers had to always return states.
No. Reducer has to always return data. Moreover, you should not return state, but a new object (or other data types).
So what is done in your case is that the reducer returns a new string (or whatever data type is text) every time HELLO_WORLD_NAME_UPDATE action is dispatched. It does not care what was already in the state and returns a new text string.

Resources