React Redux Setup for Slider Status - reactjs

I have a left Sidebar that I want to share State of throughout my application so if user clicks on slider's button, user can make it hidden or show. Initially I plan to have it shown. I was following a Redux course and in the coure they did an example of fetching posts from API which is entirely different thant what I need so I am puzzled here...
So far, I created a folder called actions with 2 files,
sliderActions.js
import { Slider } from "./types"
export function sliderUpdate() {
return function(dispatch) {
dispatch({
status: "hidden"
})
}
}
and types.js
export const Slider = "Slider";
reducers folder has two files
index.js
import { combineReducers } from "redux"
import postReducer from "./postReducer"
export default combineReducers({
posts: postReducer
})
and postReducer.js
import { Slider } from "./../actions/types"
const initialState = {
Slider: "hide"
}
export default function(state = initialState, action) {
switch(action.type) {
default:
return state;
}
}
store.js file
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)
)
export default store
and lastly
I imported below two to the App.js
import { Provider } from "react-redux"
import { store } from "./store"
and wrapped my whole code inside return statement of App with a <Provider store={store}> and </Provider>
I am entirely new to the redux and don't know how to get this working, any help would be appreciated!

Actions must have a type property that indicates the type of action being performed. Types should typically be defined as string constants.
So your actions file should be something like this
import { SLIDER } from "../constant/slide";
export function sliderUpdate() {
return function(dispatch) {
dispatch({
type: SLIDER,
status: "hidden"
});
};
}
Where the constant SLIDER
export const SLIDER = "SLIDER";
And the reducer should look like this
import { SLIDER } from "../constant/slide";
const initialState = {
Slider: "hide"
};
export default function(state = initialState, action) {
switch (action.type) {
case SLIDER:
return Object.assign({}, state, action.data);
default:
return state;
}
}
We don't mutate the state.So We create a copy with Object.assign() where it will contain the new data.

Documentation says: Actions may not have an undefined "type" property.
Change your action in your sliderUpdate function and add 'type' key.
For example:
dispatch({
type: "SLIDER_BUTTON_CLICKED",
status: "hidden",
});
and now you want to change your postReducer to:
const initialState = {
slider: "opened"
}
export default function(state = initialState, action) {
switch(action.type) {
case "SLIDER_BUTTON_CLICKED": {
return {...state, slider: action.status}
}
default:
return state;
}
}

Related

Redux combineReducers() isn't recognising state

I am using redux combineReducers() to combine 2 reducers. While they are both recognised in dev tools, one of them is simply an empty object, even though I passed an object with different properties into the reducer.
The combineReducers() file:
import { combineReducers } from 'redux'
import { default as JobReducer } from './Jobs/reducer'
import { default as AuthReducer } from './Auth/reducer'
const RootReducer = combineReducers({
job: JobReducer,
auth: AuthReducer,
})
export default RootReducer
The first reducer:
import { SINGLE_JOB_POST_SUCCESS } from './constants'
const initial = {
jobs: [],
job: {}
}
export default (state = initial, action) => {
const { type, payload } = action
switch (type) {
case SINGLE_JOB_POST_SUCCESS:
return {
...state,
jobs: [
...state.jobs,
...payload
]
}
default:
return {}
}
}
The second reducer:
import { RESET_AUTH_RESPONSE } from './constants'
const initial = {
authResponse: null,
user: {}
}
export default (state = initial, action) => {
const { type, payload } = action
switch (type) {
case RESET_AUTH_RESPONSE:
return {
...state,
authResponse: null
}
default:
return state
}
}
When I look at the state in redux dev tools, "auth" has the relevant properties, but "job" is simply an empty object. I have called the reducer files different names so as to remove the need for aliases but it had no effect. Any help is appreciated.
Because your authReducer has:
default:
return {}
So, any time it sees an action it doesn't recognize, it'll return an empty object. You need it to be return state instead.
Also, note that you should switch to using our official Redux Toolkit package, which will simplify your Redux logic considerably.

State object structure in react-redux application

I'm creating a simple blog using React and Redux, most for learning these two libraries. Everything is working fine, my only question is about the way the state object is structured in the application. When I go to the Redux Toolkit, I see this:
Redux Toolkit screenshot
In the state I have a post object with another post object inside it. My question is: where did I defined the state in that way (with a post object inside a post object)? Below are the content of this application:
MainPost.js
import React, { useEffect } from 'react'
import { connect, useDispatch } from 'react-redux'
import { getPostListAction } from '../store/actions/getPostListAction'
export const MainPost = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(getPostListAction())
})
return (
<div>App</div>
)
}
const mapStateToProps = state => {
return {
post: state.post
}
}
export default connect(mapStateToProps)(MainPost)
store.js
import { createStore, applyMiddleware, compose } from 'redux'
import rootReducer from './reducers/rootReducer'
import { devToolsEnhancer } from 'redux-devtools-extension'
import thunk from 'redux-thunk'
import { api } from './middleware/api'
const store = createStore(
rootReducer,
compose(
devToolsEnhancer(),
applyMiddleware(thunk),
)
)
export default store
postListReducer.js
const initialState = {
post: ''
}
export default function getPostListReducer(state = initialState, action) {
if(action.type === 'getPostList') {
return {
...state,
post: action.payload
}
} else {
return state
}
}
The first post (after state) is namespace of postListReducer
Here is how you use combineReducer to create a rootReducer:
const rootReducer = combineReducers({
post: postListReducer,
other: otherReducer
})
And to select data from the store, you do:
const mapStateToProps = state => {
return {
post: state.post.post // the first "post" (after "state") is namespace of postListReducer
}
}
Or if you don't want to write state.post.post, you can change your postListReducer to directly hold the "post" data:
const initialPost = ''
export default function getPostListReducer(state = initialPost, action) {
if(action.type === 'getPostList') {
return action.payload
} else {
return state
}
}

store.getState() returns reducer objects when using combineReducers

In my project, I am trying to combine two reducers but whenever I try to combine them using combineReducers({}) my props become undefined and my state ( from store.getState() ) turns out to be two objects name after my reducers. See my reducer setup
root/reducers.js
import { combineReducers } from 'redux'
import dashboardReducer from '../views/DashboardPage/redux/reducers'
import formReducer from '../views/FormPage/redux/reducers'
export default combineReducers({
dashboardReducer,
formReducer
})
configureStore.js
import { createStore, compose } from "redux";
import rootReducer from "./reducers";
const storeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(rootReducer);
export default store;
dashboard/reducer.js
import {ADD_FIELD} from "./types"
const initialState = {
fields: [{title: "bla", text: "jorge", id: 1}],
};
const dashboardReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_FIELD:
return {
...state,
fields: state.fields.concat(action.payload)
};
default:
return state;
}
};
export default dashboardReducer;
forms/reducer.js
const formReducer = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state
}
};
export default formReducer;
calling console.log on "store.getState()" and "this.props" (after mapping state to props) returns the following, respectively:
console.log's
if it matters, I am using react-router
That is correct behaviour and it sounds like you need to modify your mapStateToProps to handle it correctly, so you might want to add your mapping function to your question.
export default combineReducers({
dashboardReducer,
formReducer
})
Using combineReducers means that you will manage different slices of your state with the different reducers and these slices will be named after the keys in the object you provide. You probably want to change that to be:
import dashboard from '../views/DashboardPage/redux/reducers'
import form from '../views/FormPage/redux/reducers'
export default combineReducers({
dashboard,
form
})
this will result in your state having the shape:
{
dashboard: { dash: "things" },
form: {}
}
Your dashboard reducer will be called with state set to
{ dash: "things" }
and your mapStateToProps will need to read the state accordingly
return {
fields: state.dashboard.fields
};

Redux seems to be getting state/reducers mixed up

I'm having a really strange issue in react in which my reducers seem to be modifying parts of the state that they shouldn't, I'm sure this is due to some oversight in my implementation, but I can't track it down.
I currently have one store, two actions/action creators, two reducers, and one root reducer. Here is my code, (left out some unimportant details):
//actionTypes.ts
export const SUBMIT_SEARCH = "SUBMIT_SEARCH";
export const CHANGE_SEARCH_PAGE = "CHANGE_SEARCH_PAGE";
-
//actionCreators.ts
import { SUBMIT_SEARCH, CHANGE_SEARCH_PAGE } from "./actionTypes"
export const submitSearch = (submitSearch) => ({
type: SUBMIT_SEARCH,
submitSearch
});
export const changeSearchPage = (changeSearchPage) => ({
type: CHANGE_SEARCH_PAGE,
changeSearchPage
});
-
//submitSearchReducer.ts
import { SUBMIT_SEARCH } from "../actions/actionTypes";
const submitSearch = (state = {}, action) => {
switch (action.type) {
case SUBMIT_SEARCH:
return {
...state,
submitSearch: action.submitSearch
}
default:
return state;
};
};
export default submitSearch;
-
//changeSearchPageReducer.ts
import { CHANGE_SEARCH_PAGE } from "../actions/actionTypes";
const changeSearchPage = (state = {}, action) => {
switch (action.type) {
case CHANGE_SEARCH_PAGE:
return {
...state,
changeSearchPage: action.changeSearchPage
}
default:
return state;
};
};
export default changeSearchPage;
-
//rootReducer.ts
import { combineReducers } from 'redux';
import submitSearch from "../reducers/submitSearchReducer"
import changeSearchPage from "../reducers/submitSearchReducer";
const rootReducer = combineReducers({
submitSearch: submitSearch,
changeSearchPage: changeSearchPage
});
export default rootReducer;
-
I create the store like this:
const logger = createLogger();
const store: Store<any> = createStore(rootReducer, {/*Initial state empty*/}, applyMiddleware(logger));
-
The general flow is like this:
User enters a search string on the page which triggers a store.dispatch(submitSearch(string)) call
User enters a number on the page which triggers a store.dispatch(changeSearchPage(newPage))
Here's the output from my logger:
[![Search call][1]][1]
[![change page call][2]][2]
You can see here that the state is clearly getting mixed up, and the wrong data is going to the changeSearchPage key.
What is causing this mix up?
In the rootReducer you're importing the same reducer twice

Understanding combineReducers

New to react and redux so playing around with some very simple code to see how it all works.
When I try passing in a combineReducers method to a redux store then I get an error. If I remove the combinedReducers and pass the reducer in directly to the store all works fine.
let store = createStore(rootReducer);
Error
Uncaught Error: Objects are not valid as a React child (found: object
with keys {reducer}). If you meant to render a collection of children,
use an array instead or wrap the object using createFragment(object)
from the React add-ons. Check the render method of App.
Why do I get an error when I use combineReducers ? What if I wanted to add more reducers I presume thats what combineReducers is there for ?
main.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, combineReducers } from 'redux';
import App from './components/app';
let reducer = (state=0, action) => {
switch (action.type) {
case 'INCREASE':
return state+1
case 'DECREASE':
return state-1
default: return state
}
}
const rootReducer = combineReducers({
reducer:reducer
});
let store = createStore(rootReducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.querySelector('.container'));
//app.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
class App extends Component {
render() {
let {number, increase, decrease} = this.props
return(
<div>
<div>{number}</div>
<button onClick={e=>increase()}>+</button>
<button onClick={e=>decrease()}> - </button>
</div>
);
}
}
let mapStateToProps = state => ({
number: state
})
let mapDispatchToProps = dispatch => ({
increase: () => dispatch({type: 'INCREASE'}),
decrease: () => dispatch({type: 'DECREASE'})
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
Combine reducers takes a hash of reducers and returns a reducer. The resulting reducer represents an object of the same shape as the hash.
So, a call like this:
combineReducers({ name: nameReducer})
Would produce a state object that might look something like this:
{ name: 'Joe Shmo' }
In your example, you are producing a global state tree that looks like this:
{ reducer: 0 }
But you are trying to pull a property called number out of this in your mapStateToProps.
If you change your reducer declaration to look like this:
const number = (state=0, action) => {
switch (action.type) {
case 'INCREASE':
return state+1
case 'DECREASE':
return state-1
default: return state
}
}
const rootReducer = combineReducers({
number
});
Then change your mapStateToProps to look like this:
const mapStateToProps = ({number}) => ({number});
Your code should start working.
https://redux.js.org/docs/basics/Reducers.html
import { combineReducers } from 'redux'
const todoApp = combineReducers({
visibilityFilter,
todos
})
export default todoApp
Note that this is equivalent to:
export default function todoApp(state = {}, action) {
return {
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
todos: todos(state.todos, action)
}
}
You could also give them different keys, or call functions differently. These two ways to write a combined reducer are equivalent:
const reducer = combineReducers({
a: doSomethingWithA,
b: processB,
c: c
})
function reducer(state = {}, action) {
return {
a: doSomethingWithA(state.a, action),
b: processB(state.b, action),
c: c(state.c, action)
}
}
And don't forget connect each parts
#connect(state => ({
reducerName: state[partStoreName]
}))
I believe you could have also done:
const INITIAL_STATE = { number: 0 };
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'INCREASE':
return state+1
case 'DECREASE':
return state-1
default: return state
}
};

Resources