How to share data between two slice reducers redux toolkit - javascript

I have two slice reducers and I want to get the state of one to the other.
In the old way of redux I would do it like that:
import store from "store"
///reducer code
const state = store.getState();
Now Iam using redux-toolkit and this way of importing store doesnt work.
here is LoginSlice:
import { createSlice } from '#reduxjs/toolkit';
import axios from "axios"
import Swal from 'sweetalert2'
export const initialState = {
id: "1111"
}
export const LoginSlice = createSlice({
name: 'login',
initialState,
reducers: {
login: state => {
state.id = "2222"
},
},
});
export const { login} = LoginSlice.actions;
here is Sites slice :
import { createSlice } from '#reduxjs/toolkit';
import axios from "axios"
export const SitesSlice = createSlice({
name: 'sites',
initialState: {
sites: []
},
reducers: {
GetSites: (state,action) => {
axios.post(process.env.REACT_APP_API_URL+"/sites/get",{id: get id here}).then((err,data) =>{
if(data.data.success){
state.sites = data.data.data
}else{
error
}
})
.catch(error => {
error
})
}
},
});
export const { GetSites } = SitesSlice.actions;
here is the store:
import { configureStore } from '#reduxjs/toolkit';
import { combineReducers } from 'redux'
import loginReducer from '../features/Login/loginSlice';
import siteReducer from '../features/Sites/SitesSlice';
const reducer = combineReducers({
login: loginReducer,
sites: siteReducer,
})
export default configureStore({
reducer
});
what I want to do is to get the user id from the logIn Slice to the Sites Slice.
something like store.getState().logIn.user.id
I am having trouble with this );

You should never do any side effect within a reducer - this includes both accessing an external variable like state (as suggested by Muhammad Yaqoob in his answer) as well as executing a axios.post like you are trying there. (Also, that is not only something that should not be done - it will also never work)
This should be done in a middleware or just in a thunk. Since that is a whole new can of worms that goes well beyond the scope of this question, please see the official redux tutorial on this topic: https://redux.js.org/tutorials/essentials/part-5-async-logic
(If you follow this path you will notice that you can access current state values from within middleware, thunks and createAsyncThunk, which should then also answer your question)

The configureStore() function returns the same store object which you can simply get like this:
const store = configureStore({
reducer
})
And then use it, like you normally would.
store.getState()
You might want to export store also.
export default store;

Related

Data is not being stored in redux store

store imageI am going to store the data into the react-redux-store but it is not getting stored. I don't understand what I am missing...I have given my code below.
i am trying to store the data from the api but it is not working...
INDEX.JS
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { Provider } from "react-redux";
import { store } from "./features/store";
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
STORE.JS
import { configureStore } from "#reduxjs/toolkit";
import moviesReducer from "./movies/movieSlice";
export const store = configureStore({
reducer: moviesReducer,
});
MOVIE SLICE.JS
import { createSlice } from "#reduxjs/toolkit";
const initialstate = {
movies: [],
};
const movieSlice = createSlice({
name: "movies",
initialstate,
reducers: {
addMovies: (state, { payload }) => {
state.movies = payload;
},
},
});
export const { addMovies } = movieSlice.actions;
// export const getAllMovies = (state) => state.movies.movies;
export default movieSlice.reducer;
COMPONENT
import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
import MovieAPI from "../config/MovieAPI";
import { addMovies } from "../features/movies/movieSlice";
const Home = () => {
const dispatch = useDispatch();
const fetchMovies = async () => {
const response = await MovieAPI.get(`?apiKey=1234&s=harry&type=movie`);
console.log(response.data);
dispatch(addMovies(response.data));
};
useEffect(() => {
fetchMovies();
}, []);
For the very first: createSlice expecting to recieve object with property named initialState instead initialstate, notice camelCase naming.
The next one: acording to location and slice name "movies" I may suspect you should define it as: const initialState = [];, due to it is "movies slice" initial state definition itself, otherwise you will have state with something like
state = {movies: {movies: []}}.
Also, you may wish to rewrite addMovies reducer in something like:
addMovies: (moview_slice_state, { payload }) => {
console.log("add movies", payload);
moview_slice_state.push(...payload);
}
where moview_slice_state - state of movies slice of whole state, e.g. state.movies.
By the way, due to #reduxjs/toolkit use immer under the hood you may "modify" state OR return new state, as Andrej Kirejeŭ propose. But NOT the both of them.
P.S. For the future, feel free to create minimal demo for your question or answer, some thing like live demo based on your code
return new state:
addMovies: (state, { payload }) => ({
...state,
movies: payload
}),
by the way, how do you know it is not stored. Please, show the code where you use state data to render some component.

Redux configureStore erros after adding another reducer

my current store looks like this:
import { configureStore } from "#reduxjs/toolkit";
import notesReducer from "./notesReducer";
import todoReducer from "./todoReducer";
const store = configureStore({
reducer: todoReducer, // here i wanna add a notesReducer
});
export default store;
As said, i wanna add a notesReducer and what i have seen in tutorial i could do it by that:
const store = configureStore({
reducer: { todo: todoReducer, note: notesReducer },
});
But this althoug, deploy an errror in my Todos.js file: TypeError: props.todos.map is not a function
Whole Todos.js file so you could help me finding the solution or things I did wrong:
https://pastebin.com/AWyg8BXW
Shouldn't your mapStateToProps be
const mapStateToProps = (state) => {
return {
todos: state.todos,
};
};

CreateAsyncThunk Error: Actions must be plain objects. Use custom middleware for async actions

I am currently setting up my RTK (Redux Toolkit) and did some minor testings. Here's my code:
store/index.js
import { configureStore } from '#reduxjs/toolkit'
import { loginSliceReducer } from './views/page/login/loginSlice'
export default configureStore({
reducer: {
login: loginSliceReducer
}
})
loginSlice.js
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit'
import ApiService from '../../services/ApiService'
export const authorize = createAsyncThunk(
'api/authorize',
async (email, password) => {
const response = await ApiService.post(email, password)
return response.data
}
)
export const loginSlice = createSlice({
name: 'login',
initialState: {
loading: true,
token: null,
data: []
},
reducers: {
updateState: (state, action) => {
const { payload } = action
switch (payload.type) {
case AUTH_SUCCESS:
state.loading = false
state.token = payload.token
state.data = payload.data
break
default:
}
}
},
extraReducers: {
[authorize.fulfilled]: (state, action) => {
// ... do state update here
}
}
})
export default loginSlice.reducer
login.js
import React, { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { authorize } from './loginSlice'
const Login = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(authorize('testuser#example.com', 'test123'))
}, [])
return <div>Auth Test</div>
}
The code above doesn't work. I keep getting this error:
Error: Actions must be plain objects. Use custom middleware for async actions.
On this line:
> 25 | dispatch(authorize('testuser#example.com', 'test123'))
Please don't mind me triggering the authorize on useEffect, as this is only a test to check if the endpoint is being called and to check if the state will update once the request is successful. :-D
I had this same issue and it was caused by the fact that I was adding additional middleware.
#reduxjs/toolkit's configureStore has some middleware that is included by default, but if you add anything to the middleware property, it will overwrite these defaults.
The solution is to include the default middleware along with the middleware you define:
import { configureStore, getDefaultMiddleware } from '#reduxjs/toolkit';
import { loginSliceReducer } from './views/page/login/loginSlice';
import { otherMiddleware } from './middlewares/some-other-module';
export default configureStore({
reducer: {
login: loginSliceReducer
},
middleware: [ // Because we define the middleware property here, we need to explictly add the defaults back in.
...getDefaultMiddleware(),
otherMiddleware
]
})
Note, there is no need to explicitly include redux-thunk when using #reduxjs/toolkit because it is already part of the default middlewares from getDefaultMiddleware()
Looks like the problem is that you didn't add to your store a middleware capable of handling async actions
In your store/index.js try smth like:
import { applyMiddleware } from 'redux';
import { configureStore, getDefaultMiddleware } from '#reduxjs/toolkit'
import { loginSliceReducer } from './views/page/login/loginSlice'
import thunk from 'redux-thunk';
export default configureStore({
reducer: {
login: loginSliceReducer
},
middleware: [applyMiddleware(thunk), getDefaultMiddleware()]
})
If there are more than one argument to be passed to an action creator, you must pass them inside an object.
For example, if I have to send email and password as payload, I will have to send them in an object like below:
dispatch(authorize({email, password}))
one way you can fix this is to use your store's dispatch method.
This way, since thunk is included in Redux Toolkit (RTK) by default, you'll have access to that - which should fix the error you're getting.
Try This:
store/index.js
import { configureStore } from '#reduxjs/toolkit'
import { loginSliceReducer } from './views/page/login/loginSlice'
export default const store = configureStore({
reducer: {
login: loginSliceReducer
}
})
const useAppDispatch = () => store.dispatch
export { useAppDispatch }
login.js
import React, { useEffect } from 'react'
import { useSelector} from 'react-redux'
import { authorize } from './loginSlice'
import { useAppDispatch } from './store/index'
const Login = () => {
const dispatch = useAppDispatch()
useEffect(() => {
dispatch(authorize('testuser#example.com', 'test123'))
}, [])
return <div>Auth Test</div>
}

In Home component of react+redux app, this.props.posts is undefined after calling action getPosts() on componentdidmount

I am trying to make a blog-style application. I am running a django server and trying to make a react-redux frontend. I am using redux-devtools and when I comment out the error code, redux seems to have the data in the state. Not sure what is going wrong. Also, I am using redux-thunk and axios to communicate with the backend. I am pretty much copying from a youtube tutorial.
This is the reducer reducers/posts.js
import {GET_POSTS} from "../actions/types";
const initialState = {
posts: []
}
export default function(state = initialState, action) {
switch (action.type) {
case GET_POSTS:
return {
...state,
posts: action.payload
}
default:
return state;
}
}
this is the action actions/posts.js
import axios from "axios";
import {GET_POSTS} from "./types";
export const getPosts = () => dispatch => {
axios.get('/get/posts/').then(res => {
dispatch({
type: GET_POSTS,
payload: res.data
})
}).catch(error => {console.log(error)})
}
this is reducers/index.js
import {combineReducers} from 'redux';
import posts from "./posts";
export default combineReducers({
posts
});
this is store.js
import {createStore, applyMiddleware} from 'redux';
import {composeWithDevTools} from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
this is components/Home.js (ERROR HERE)
import React from 'react';
import {connect} from 'react-redux';
import {getPosts} from '../actions/posts';
class Home extends React.Component {
componentDidMount() {
this.props.getPosts();
}
render() {
console.log(this.props.posts); //undefined
const posts = this.props.posts.map(post => (
<div className="post">
<h1>{post.title}</h1>
<p>{post.message}</p>
</div>
)
)
return (
{posts}
)
}
}
const mapStateToProps = state => ({
posts: state.posts // I have tried state.posts
// and state.posts.posts. I think
//state.posts might be undefined which is
//causing this.props.posts to be undefined in the component
});
export default connect(mapStateToProps, {getPosts})(Home);
ANSWER: I found out that render is called multiple times and the first time it is called, this.props.posts is undefined, so I put an if statement for if this.props.posts is undefined and if it is not, I render the list.
On your return inside Home component do this:
return (
{this.posts.length > 0 && posts}
)
Your initial render, is trying to render an array of elements that are not being yet fetched from the server. All fetch calls to web apis and to backend servers are async which means they will execute as soon as the stack frame of the JS engine is free. Adding the conditional statement will allow you to do an extra checkwhich will rerender the page since the data will be fetched and stored to your reducer. Additionally, when not sure what is the state/shape of your redux store, you can always console log it like this:
const mapStateToProps = state => {
console.log(state);
return {
posts: state.posts
}
}

Enhanced object literal in ES6 with react not working

I am trying to use enhanced object literal syntax in my redux reducer, and some of them are working while others return empty objects. Is there a reason why 'form', and 'auth' work fine in my example, but 'files' does not? When I change 'files' to the traditional syntax, it works just fine. When I execute the example below, it just returns an empty object
import { combineReducers } from 'redux';
import auth from './auth_reducer';
import userReducer from './user_reducer';
import propertyReducer from './property_reducer';
import files from './file_upload_reducer';
import { reducer as form } from 'redux-form';
const appReducer = combineReducers({
/* your app’s top-level reducers */
form,
auth,
user: userReducer,
properties: propertyReducer,
files
})
const rootReducer = (state, action) => {
if (action.type === 'UNAUTH_USER') {
state = undefined
}
return appReducer(state, action)
}
export default rootReducer;

Categories