I am using the React Hooks implementation of React-Redux. Below is the flow of my code. For some reason any values that I pass in my dispatch(fuction(value)) is not detected in my reducer. I can't figure it out.
src/components/categories/selectCategory.tsx
import React from 'react';
import {useDispatch} from 'react-redux';
import {setCategory} from '../../store/actions';
const selectCategory = (name: string) => {
dispatch(setCategory(name)); // ex: name = 'algebra'
};
store/actions/index.ts
export const setCategory = (name) => ({
type: 'SET_CATEGORY',
name: name
});
store/reducers/index.ts
import {combineReducers} from 'redux';
import category from './category';
const app = combineReducers({
category
});
export default app;
store/reducers/category.ts
const initialState = {
name: 'calculus'
};
const category = (state = initialState, action) => {
switch (action.type) {
case 'SET_CATEGORY':
return {name: state.name}; // outputs 'calculus'
default:
return state;
}
};
export default category;
I'm sure there is some small detail I am missing.
My issue was fixed by returning the action.name property instead of state.name.
store/reducers/category.ts
const initialState = {
name: 'calculus'
};
const category = (state = initialState, action) => {
switch (action.type) {
case 'SET_CATEGORY':
return {name: action.name};
default:
return state;
}
};
export default category;
Related
I just started using Redux-toolkit in my project. One thing I came across is stated in the title.
Slice Component
import { createSlice } from '#reduxjs/toolkit'
const followingSlice = createSlice({
name: 'followings',
initialState:{
followings_list: []
},
reducers:{
addFollowing(state, action) {
const user = action.payload.user
state.followings_list.push(user)
},
removeFollowing(state, action){
const id = action.payload.user.id_str
return state.followings_list.filter(following =>
following.id_str!==id)
}
}
})
export const { addFollowing, removeFollowing} = followingSlice.actions
export const selectFollowings = (state) => state.following.followings_list
export default followingSlice.reducer
Store Component
import { configureStore } from '#reduxjs/toolkit'
import FollowingsReducer from '../reducers/FollowingsSlice'
export default configureStore({
reducer:{
following: FollowingsReducer
}
})
Essentially there are two buttons, one is to add an item to the followings_list, another one is to remove from it. But if I remove an 'item', even before there is an item in the followings_list, my followings_list state becomes undefined or at least it disappears from the following reducer?
import {useDispatch } from 'react-redux';
import{
removeFollowing,
} from "../reducers/FollowingsSlice";
const dispatch = useDispatch()
// this line is called when the button is clicked, I omit other codes
dispatch(removeFollowing({something}))
Before I called the remove action:
After I called the action:
Hi~Have you already solved it?
I checked your code.
in your Slice Component code
import { createSlice } from '#reduxjs/toolkit'
const followingSlice = createSlice({
name: 'followings',
initialState:{
followings_list: []
},
reducers:{
addFollowing(state, action) {
const user = action.payload.user
state.followings_list.push(user)
},
removeFollowing(state, action){
const id = action.payload.user.id_str
return state.followings_list.filter(following =>
following.id_str!==id)
}
}
})
export const { addFollowing, removeFollowing} = followingSlice.actions
export const selectFollowings = (state) => state.following.followings_list
export default followingSlice.reducer
The part corresponding to the following in the above slice component code
removeFollowing(state, action){
const id = action.payload.user.id_str
return state.followings_list.filter(following =>
following.id_str!==id)
}
Instead of 'return' the filter value right away, you should assign the filtered result value to the state.
You can do it by changing the code like below
removeFollowing(state, action){
const id = action.payload.user.id_str;
let filtered = state.followings_list.filter(following =>
following.id_str!==id);
state.followings_list = filtered;
}
Hope this helps you!
I am unable to access my state values saved in store on any screen. values reach to actions but when I access it from store it returns always undefined.
Every thing is in separate files
Reducer 1
import * as Actions from '../actionTypes'
import initialStore from './initialStore'
const homeModuleReducer = (state = initialStore, action) => {
switch (action.type) {
case Actions.SET_PROFILE_ONE:
console.log('call here')
return {
...state,
currentUser: action.profile
}
default:
return state;
}
}
export default homeModuleReducer
Reducer 2
import * as Actions from '../actionTypes'
import initialStore from './initialStore'
const loginModuleReducer = (state = initialStore, action) => {
switch (action.type) {
case Actions.SET_PROFILE:
return {
...state,
currentUser: action.profile
}
case Actions.SET_INITIAL_LOADING_STATUS:
return {
...state,
isInitialLoadingDone: action.loadingStatus
}
default:
return state;
}
}
export default loginModuleReducer
Combine Reducer
import { combineReducers } from 'redux'
import homeModuleReducer from './homeModuleReducer'
import loginModuleReducer from './loginModuleReducer'
export default combineReducers({
homeModuleReducer,
loginModuleReducer,
})
Store
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk';
import rootReducer from './reducers'
let store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
export default store;
usage:
const mapStateToProps = (state) => ({
stateLoaded: state.rootReducer.isInitialLoadingDone,
profile: state.rootReducer.currentUser
});
Error:
undefined is not an object (evaluating 'state.rootReducer.isInitialLoadingDone')
You already combined your reducers so you can access reducer by it's key like this :
const mapStateToProps = (state) => ({
stateLoaded: state.homeModuleReducer.isInitialLoadingDone, // here homeModuleReducer is just an example. Change with reducer key in which isInitialLoadingDone is belong
profile: state.loginModuleReducer.currentUser
});
With hooks its much easier
wrap your root with store
import {Provider} from 'react-redux';
const App = () => {
return (
<Provider store={store}>
<Yourcomponent/>
</Provider>
);
};
export default App;
Access your state in any component like this
import { useSelector } from "react-redux";
const state = useSelector(state => state)
I am trying to display the redux state into my react component, but it comes undefined.
I am unable to understand where am I doing the mistake.
I am learning redux by trying a coding on my own by going through the redux documentation.
Main React component
import React, { Component } from 'react';
import Counter from './components/Counter';
import {Provider} from 'react-redux';
import store from './redux/store';
class App extends Component {
render() {
return (
<Provider store={store}>
<div>
<h1>COUNTER APPlICATION</h1>
<Counter />
</div>
</Provider>
)
}
}
export default App;
React Component
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {addNumber} from '../redux/actions/addAction';
import {substractNumber} from '../redux/actions/substractAction';
export class Counter extends Component {
render() {
return (
<div>
<h1>Value:{this.props.value}</h1>
<h1>Add Only Value:{this.props.addOnly}</h1>
<button onClick = {() => this.props.addNumber}>+</button>
<button onClick = {() => this.props.substractNumber}>-</button>
</div>
)
}
}
const mapStateToProps = state => ({
value: state.value
});
export default connect(mapStateToProps, {addNumber, substractNumber})(Counter);
addReducer
import {ADDITION} from '../actions/actionTypes';
const initialState = {
value: 50
}
export default function (state = initialState, action) {
switch(action.type){
case ADDITION:
return{
value: state.value + 2
}
default:
return state
}
}
substractReducer
import {SUBSTRACTION} from '../actions/actionTypes';
const initialState = {
value: 50
}
export default function (state = initialState, action) {
switch (action.type) {
case SUBSTRACTION:
return {
value: state.value - 2
}
default:
return state
}
}
rootReducer
import {combineReducers} from 'redux';
import addReducer from './addReducer';
import substractReducer from './substractReducer';
export default combineReducers({
add: addReducer,
substract: substractReducer
})
store
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers/rootReducer';
import thunk from 'redux-thunk';
export default createStore(rootReducer, applyMiddleware(thunk));
action type
export const ADDITION = 'ADDITION';
export const SUBSTRACTION = 'SUBSTRACTION';
addAction
import {ADDITION} from './actionTypes';
export const addNumber = () => (dispatch) => {
return dispatch({
type: ADDITION,
payload: 2
})
}
substractAction
import {SUBSTRACTION} from './actionTypes';
export const substractNumber = () => (dispatch) => {
return dispatch({
type: SUBSTRACTION,
payload: 2
})
}
You are doing wrong.
you state is just counter value, so don't split into two reducers. You only need two case statement, one for ADD, one for SUBTRACT.
Don't use combineReducer and it you want, use one key like counter for counter reducer
in mapStateToProp, get value like state.counter.value where counter is name of key you used in combineReducer({ counter: counterReducer })
Your button actions/onclick is wrong
import {ADDITION, SUBTRACTION} from '../actions/actionTypes';
const initialState = {
value: 50
}
export default function (state = initialState, action) {
switch(action.type){
case ADDITION:
return
value: state.value + 2
}
case SUBTRACTION:
return{
value: state.value + 2
}
default:
return state
}
}
///// no need to regester 2 reducer, just add one above like this
export default combineReducers({
counter: counterReducer
});
/// In Counter component , mapStateToProp
const mapStateToProps = state => ({
value: state.counter.value
});
// Just pass redux actions to onClick in button like this
<button onClick = {this.props.addNumber}>+</button>
<button onClick = {this.props.substractNumber}>-</button>
When you combineReducers like this:
export default combineReducers({
add: addReducer,
substract: substractReducer
})
Your state tree will look like:
{
add: {
value: 0
},
subtract: {
value: 0
}
}
So you should only have a single reducer in order to reduce over the same value.
The State
The tracing results
Each time when I click the button, the dispatch run twice, like the picture above.
This is the AppBar component and mapDispatchToProps function.
const mapStateToProps = state => {
return {
title: state.title
};
};
const mapDispatchToProps = {
onClick: () => {
return {
type: "TOGGLE_SIDEBAR"
};
}
};
const AppBar = props => (
<Box>
<Button icon={<Notification />} onClick={props.onClick} />
</Box>
);
const AppBatContainer = connect(
mapStateToProps,
mapDispatchToProps
)(AppBar);
export default AppBatContainer;
This is the reducer
import {
TOGGLE_SIDEBAR
} from "../constants/action-types";
const initialState = {
showBar: false
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case TOGGLE_SIDEBAR:
return Object.assign({}, state, {
showBar: !state.showBar
});
default:
return state;
}
};
export default rootReducer;
This is the store.js
import { createStore, applyMiddleware, compose } from "redux";
import reduxThunk from "redux-thunk";
import rootReducer from "./reducers/index";
const composeEnhancers =
typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
trace: true,
traceLimit: 20
})
: compose;
const enhancer = composeEnhancers(applyMiddleware(reduxThunk));
const store = createStore(rootReducer, enhancer);
export default store;
All libraries are well imported. I tried remove redux-thunk, it is still the same result.
Thanks in advance.
Ref
React-Redux Counter example action firing twice I have viewed this questions on stackoverflow, and tried the answers, but no of them solved it.
I have to write mapStateToProps like below
function mapStateToProps(state, ownProps) {
return {
node: ownProps.info? state.TreeNodeReducer.tree[ownProps.info.path] : {}
};
}
combine reducer:
import { combineReducers } from 'redux';
import TreeNodeReducer from './TreeNodeReducer'
const rootReducer = combineReducers({
TreeNodeReducer
});
export default rootReducer;
reducers/TreeNodeReducer.js
import { OPEN_NODE, CLOSE_NODE, GET_NODES } from '../constants/NodeActionTypes';
const initialState = {
open: false,
info: {}
}
class NodeModel {
constructor(path, type, right) {
this.name = path;
this.path = path;
this.type = type;
this.right = right;
}
}
let lastId = 0;
function getFileList() {
var testNodes = []
for (var i=0; i< 3; i++) {
testNodes.push(new NodeModel(lastId,'d', i.toString()))
lastId++;
}
return testNodes
}
const getNodes = (state, action) => {
var { path } = action
var tree = state.tree ? state.tree : {}
tree[path] = getFileList(path)
return {
...state,
tree:tree
};
};
export default function (state = initialState, action) {
switch (action.type) {
case OPEN_NODE:
return { ...getNodes(state, action), open:true };
case GET_NODES:
return getNodes(state, action);
case CLOSE_NODE:
return {
...state,
open:false
};
default:
return state;
}
}
because state.TreeNodeReducer.tree is a global state, which hold all node info, I want to access it by state directly.State return by a reducer would wrap by the reducer's name, which is not convenient for simple project. Office doc don't provide the way.Any idea?
PS: I have to say I want to keep using combineReducers, I see some project not use it, directly pass reducer to store which can achieve my purpose but not good.
Achieving what you desire depends a bit on the state handled by the TreeNodeReducer.
If that reducer just handles the tree property, like this:
function treeNodeReducer(state = initialState, action) {
switch (action.type) {
case SOME_ACTION:
return Object.assign({}, state, { tree: action.tree, ... })
default:
return state
}
}
I'd say change the reducer to eliminate the tree property and merge it directly with the state, like this:
function treeNodeReducer(state = initialState, action) {
switch (action.type) {
case SOME_ACTION:
return Object.assign({}, state, action.tree)
default:
return state
}
}
This way we can access the tree object in mapStateToProps with state.treeNodeReducer.
But this is still not what we want. We want to rename treeNodeReducer to tree. There are two solutions for that:
Either:
import { combineReducers } from 'redux';
import TreeNodeReducer from './TreeNodeReducer'
const rootReducer = combineReducers({
tree: TreeNodeReducer
});
export default rootReducer;
Or:
import { combineReducers } from 'redux';
import tree from './TreeNodeReducer'
const rootReducer = combineReducers({
tree
});
export default rootReducer;
This way we can access the tree object in mapStateToProps with state.tree.