Redux configureStore erros after adding another reducer - javascript

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,
};
};

Related

How to get Redux DevTools to work with redux toolkit and next.js (with typescript)?

The innitial state shows in the DevTools but nothing else, the actions taken after the code has rendered do not show up.
in pages/_app.tsx I have this
import getStore from '../store/store'
export default function MyApp({ Component, pageProps }: AppProps) {
const store = getStore(pageProps.initialState);
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
);
}
If it wasn't for the above setup (where I needed to pass props before initialising the state) the #Firmino Changani would be right, but I can't run getStore at the store because I wouldn't get the initial state
Here's the store
import { configureStore, ThunkAction, Action, combineReducers } from "#reduxjs/toolkit";
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import UISlice from '#todoSlice/todoSlice'
const rootReducer = combineReducers({
todo: UISlice,
});
export default function getStore(incomingPreloadState?: AppState) {
const store = configureStore({
reducer: rootReducer,
preloadedState: incomingPreloadState,
});
return store;
}
export type AppState = ReturnType<typeof rootReducer>;
export type AppDispatch = ReturnType<typeof getStore>["dispatch"];
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
AppState,
unknown,
Action<string>
>;
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<AppState> = useSelector;
Here's the page itself
import type { NextPage } from 'next'
import getStore, { useAppDispatch, useAppSelector, AppState } from '#store/store'
import { intidialTodos } from '#todoSlice/todoSlice'
export async function getServerSideProps() {
const store = getStore();
await store.dispatch(intidialTodos());
return {
props: {
initialState: store.getState(),
},
};
}
const Home: NextPage = () => {
const dispatch = useAppDispatch();
const categories = useAppSelector( ( state: AppState ) => state.todo.categories );
const addTodo = () => dispatch(addTodos({name: "The one", id: 506}))
return (
<><button onClick={addTodo}>Add!</button>
.....
)}
I think we can't expect the intidialTodos triggered from getServerSideProps to in the actions pannel of dev tools, But when I click the add button, I should see the action in the dev tools and I should see the new added item in the state, right?
The app works, the new item gets added and everything but nothing after ##INIT happens in the Redux dev tools
I tried this but did not work:
import { composeWithDevTools } from 'redux-devtools-extension';
import UISlice from '#todoSlice/todoSlice'
import {createAsyncThunk} from '#todoSlice/todoSlice';
const rootReducer = combineReducers({
todo: UISlice,
});
export default function getStore(incomingPreloadState?: AppState) {
const composeEnhancers = composeWithDevTools({ actionCreators: [createAsyncThunk], trace: true, traceLimit: 25 });
const store = configureStore({
reducer: rootReducer,
preloadedState: incomingPreloadState,
devTools: false,
enhancers: [composeEnhancers({ realtime: true, port: 8000 })],
});
return store;
}
You have devTools set to false. I have the following setup on a Next app:
import { createLogger } from "redux-logger";
const logger = createLogger();
export function makeStore() {
return configureStore({
devTools: true,
middleware: [logger],
reducer: {/* My reducers */},
});
}
const store = makeStore();
Please use next-redux-wrapper instead of reinventing that yourself. That package already does all you are trying to do here.

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.

How to share data between two slice reducers redux toolkit

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;

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>
}

this.props.*** is not a function

I am new to this React Redux thing.
I was trying to replicate the same tutorial from Traversy Media Youtube channel
Github: https://github.com/bradtraversy/lead_manager_react_django
and encounter some problem with this.props.function
I have tried reading around the Stackoverflow but I was not using mapDispatchToProps and I don't really understand it as I am trying to understand the tutorial
Music.js
export class Music extends Component {
static propTypes = {
musics: PropTypes.array.isRequired,
getMusics: PropTypes.func.isRequired
};
componentDidMount() {
console.log(this.props)
this.props.getMusics();
};}
const mapStateToProps = state => ({
musics: state.musics.musics
});
export default connect(
mapStateToProps, {getMusics}
)(Music);
I stripped the rendering part because I haven't implemented
types.js
export const GET_MUSICS = "GET_MUSICS"
actions.js
import axios from 'axios';
import {GET_MUSICS} from './types';
export const getMusics = () => dispatch => {
axios.get('/api/musics/').then(res => {
dispatch({type: GET_MUSICS, payload: res.data});
}).catch(err=> console.log(err));
}
reducer musics.js
import {GET_MUSICS} from '../actions/types.js';
const initialState = {
musics: []
}
export default function (state = initialState, action) {
switch (action.type) {
case GET_MUSICS:
return {
... state,
musics: action.payload
};
default:
return state;
}
}
This will throw the error this.props.getMusics is not a function
console.log(props) return empty object
You are exporting Music both as named export and default export. Make sure to use the default export because that is connected to store. i.e do this,
import Music from "./Music"
and not this,
import {Music} from "./Music"
I think it will help u try this
import {bindActionCreators} from 'redux'
...
function mapDispatchToProps(state) {
return {
musics: state.musics.musics
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({fetchMusics}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(Music)
pls check the parent component you might not be passing the function correctly,also more code is required showing the parent component

Categories