I'm new in Redux and I've got some issues learning it.
If I call a selector before a dispatch it becomes in an infinite loop, but if I call the selector after the dispatch action it works fine. I've followed documentations steps and look through stack-overflow and couldn't find some issue similar.
I let the code below:
selector:
import { createSelector } from '#reduxjs/toolkit';
import { RootState } from '#/store';
export const selectCount = (state: RootState) => state.learningLanguages;
export const countSelector = createSelector(selectCount, state => state);
action:
import {createAction} from "#reduxjs/toolkit"
import {LearningLanguagesType} from "#/globals";
export const storeLearningLanguages = createAction<LearningLanguagesType[]>('languages/store')
reducer
import {
createReducer, createSlice, PayloadAction,
} from '#reduxjs/toolkit';
import { LearningLanguagesType } from "#/globals";
import {storeLearningLanguages} from "#/store";
const initialState: LearningLanguagesType[] = [{
id: 'en',
name: 'english',
level: '0'
}];
export const learningLanguagesReducer = createReducer(initialState, builder => {
builder
.addCase(storeLearningLanguages, (state, action) => {
return action.payload;
});
});
hooks
import {
TypedUseSelectorHook,
useDispatch,
useSelector,
} from 'react-redux';
import type {
AppDispatch,
RootState,
} from '.';
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
store
import {
Action,
configureStore,
ThunkAction,
} from '#reduxjs/toolkit';
import {learningLanguagesReducer} from './app/languages/reducer'
export const index = configureStore({
reducer: {
learningLanguages : learningLanguagesReducer
},
});
export type AppDispatch = typeof index.dispatch;
export type RootState = ReturnType<typeof index.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>;
execution
import {
useAppDispatch,
useAppSelector,
} from '#/store/hooks'
import {LearningLanguagesType} from "#/globals";
const Home = () => {
const dispatch = useAppDispatch();
const testLang: LearningLanguagesType[] = [
{
id: 'es',
name: 'Spain',
level: '0'
},
{
id: 'en',
name: 'English',
level: '0'
}
];
console.log('before',useAppSelector(countSelector))
console.log('dispatch', (dispatch(storeLearningLanguages(testLang))))
console.log('after',useAppSelector(countSelector))
}
This way works perfectly, and it doesn't matter how many times I call the selector after the dispatch:
//console.log('before',useAppSelector(countSelector))
console.log('dispatch', (dispatch(storeLearningLanguages(testLang))))
console.log('after',useAppSelector(countSelector))
Thanks anyway.
Related
Slice
import { createSlice } from '#reduxjs/toolkit';
const initialState: { value: number } = { value: 0 };
export const testSlice = createSlice({
name: 'test',
initialState,
reducers: {
test: (state) => {
state.value += 1;
},
},
});
export const { test } = testSlice.actions;
Combine
import { combineReducers } from '#reduxjs/toolkit';
import { authSlice } from './auth';
import { productsSlice } from './products';
import { testSlice } from './test';
import { userSlice } from './user';
export const rootReducer = combineReducers({
user: userSlice.reducer,
auth: authSlice.reducer,
products: productsSlice.reducer,
test: testSlice.reducer,
});
export type RootState = ReturnType<typeof rootReducer>;
Store
import { configureStore } from '#reduxjs/toolkit';
import storage from 'utils/storage';
import { rootReducer } from './rootReducer';
declare global {
interface Window {
store: any;
}
}
const reHydrateStore = () => {
if (storage.getToken() !== null) {
return storage.getToken();
}
return false;
};
// !TODO add types
// #ts-ignore
const localStorageMiddleware = (store) => (next) => (action) => {
if (action.type.match('auth')) {
const result = next(action);
storage.setToken(store.getState().auth.token);
return result;
}
return next(action);
};
export const store = configureStore({
reducer: rootReducer,
preloadedState: {
auth: {
token: reHydrateStore() || '',
loading: false,
},
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(localStorageMiddleware),
});
window.store = store.getState();
When I output store.test to the console, the value remains old, but auth reducer works correctly.
Also I see that the state after the update in the reducers is 0.
Why is state not being updated?
Above are examples of my code:
Сreate Slice
Combine reducers
Store settings (and output store in console)
I am trying to store users fetched by random users api and trying to dispatch it to my store i have store users with an empty array, when i am trying to get users using useSelector i am getting an undefined object.
Here is my store.js:
import { configureStore } from "#reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";
import userReducer from "../features/users/userSlice";
export const store = configureStore({
reducer: {
counter: counterReducer,
user: userReducer,
},
});
Here is userSlice.js
import { createSlice } from "#reduxjs/toolkit";
export const userSlice = createSlice({
name: "users",
initialState: {
userArray: [],
},
reducers: {
getUsers: (state, action) => {
state.userArray = action.payload;
}
}
})
export const { getUsers } = userSlice.actions;
export const selectUsers = (state) => state.users.userArray;
export default userSlice.reducer;
Here is App.js
import logo from './logo.svg';
import './App.css';
import {useSelector, useDispatch } from 'react-redux';
import { getUsers, selectUsers } from './features/users/userSlice';
function App() {
const dispatch = useDispatch();
const users = useSelector(selectUsers);
const fetchUsers = async () => {
fetch("https://gorest.co.in/public/v2/users")
.then((response) => response.json())
.then((data) => {
console.log("data=====", data);
dispatch(getUsers(data));
});
};
return (
<div className="App">
<header className="App-header">
<button onClick={fetchUsers}>Get Users</button>
{users.length > 0 &&
users.map((user) => {
<li>user.name</li>;
})}
</header>
</div>
);
}
export default App;
you are mixing up state.user and state.users
either rename user in configureStore to users or use state.user.userArray
I've looked on other posts and it seemed to be a naming export thing, but I looked over and over my code and can't figure it out. I will appreciate any advice.
this is the error I'm getting:
TypeError: (0 , _redux_wetherSlice__WEBPACK_IMPORTED_MODULE_2__.setLocalLatitude) is not a function
at localWether.js:14
slice component
import { createSlice } from "#reduxjs/toolkit";
export const wetherSlice = createSlice({
name: "wether",
initialState: {
localLocation: {
latitude: "",
longitude: "",
},
reducers: {
setLocalLatitude: (state, action) => {
state.localLocation.latitude = action.payload;
},
setLocalLongitude: (state, action) => {
state.localLocation.longitude = action.payload;
},
},
},
});
export const { setLocalLongitude, setLocalLatitude } = wetherSlice.actions;
export default wetherSlice.reducer;
react component
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { setLocalLatitude, setLocalLongitude } from "../../redux/wetherSlice";
const LocalWether = () => {
const dispatch = useDispatch();
const { localLocation } = useSelector((state) => state.wether);
useEffect(() => {
navigator.geolocation.getCurrentPosition((position) => {
try {
console.log(position);
dispatch(setLocalLatitude(position.coords.latitude));
dispatch(setLocalLongitude(position.coords.longitude));
} catch (err) {
console.log(err);
}
});
}, []);
console.log(localLocation);
return <button>Click For Location</button>;
};
export default LocalWether;
store
import { configureStore } from "#reduxjs/toolkit";
import wetherReducer from "../redux/wetherSlice";
const store = configureStore({
reducer: { wether: wetherReducer },
});
export default store;
Please tell me why, when I call this.props.getOnEvents(), an error occurs that “getOnEvents() is not a function”, what’s wrong here and how to fix it?
I’m looking at the console.log and there is this function in the props, but when I try to call, an error flies out that it’s not a function
EventCalendar.js
import React, { Component } from 'react';
import { withStyles } from '#material-ui/core/styles';
import EventCalendarTable from './EventCalendarTable';
import EventCalendarView from './EventCalendarView';
const styles = theme => ({
root: {
width: '80%',
margin: '20px auto 0'
},
});
class EventCalendar extends Component {
constructor(props) {
super(props);
this.state = {
viewEvents: 'table'
};
}
componentDidMount() {
this.props.onGetEvents();
}
changeEventsView = (value) => {
this.setState({ viewEvents: value });
}
render() {
console.log(this.props);
const { classes } = this.props;
return (
<div className={classes.root}>
<EventCalendarView changeEventsView={this.changeEventsView}/>
{
this.state.viewEvents === 'table'
? <EventCalendarTable />
: <div>test</div>
}
</div>
);
}
}
export default withStyles(styles)(EventCalendar);
EventPage/component.jsx
import React from 'react';
import EventCalendar from '../../components/EventCalendar';
import './index.scss';
function Events(props) {
return (
<React.Fragment>
<EventCalendar props={props}/>
</React.Fragment>
);
}
export default Events;
EventPage/container.js
import { connect } from 'react-redux';
import EventsPage from './component';
import { getEvents } from '../../../store/modules/Events/operations';
const mapStateToProps = ({ Events }) => ({
Events
});
const mapDispatchToProps = dispatch => ({
onGetEvents: () => {
console.log(123);
dispatch(getEvents());
}
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(EventsPage);
Events/actions.js
import * as types from './types';
export const eventsFetch = value => ({
type: types.FETCHING_EVENTS,
payload: value
});
export const setEvents = ({ objById, arrayIds }) => ({
type: types.SET_EVENTS,
payload: {
eventById: objById,
eventsOrder: arrayIds
}
});
Events/types.js
export const FETCHING_EVENTS = 'Events/FETCHING_EVENTS';
export const SET_EVENTS = 'Events/SET_EVENTS';
Events/operation.js
import FetchClient from 'app/utils/FetchClient';
import IdsAndByIds from 'app/utils/IdsAndByIds';
import { eventsFetch, setEvents } from './actions';
export const getEvents = () => async (dispatch) => {
try {
const { data } = await FetchClient.get('/events');
dispatch(setEvents(IdsAndByIds(data)));
dispatch(eventsFetch(false));
} catch (error) {
console.log(error);
}
};
Events/reducer.js
import { createReducer } from 'store/utils';
import * as types from './types';
const usersInitState = {
fetching: true,
events: {
eventById: null,
usersOrder: null
},
error: null
};
const eventsReducer = createReducer(usersInitState)({
[types.FETCHING_EVENTS]: (state, { payload }) => ({
...state,
fetching: payload
}),
[types.SET_EVENTS]: (state, { payload }) => ({
...state,
events: {
...payload
}
})
});
export default eventsReducer;
Events/index.js
import { combineReducers } from 'redux';
import * as eventsListOperations from './operations';
import reducer from './reducers';
const EventsReducer = combineReducers({
eventsList: reducer
});
export default EventsReducer;
export { eventsListOperations };
The issue here is a minor one, Since you are connecting Events component to connect, you are receiveing the prop onGetEvents in that component, Now inside this component you are passing the props by a name props to the EventCalendar component
<EventCalendar props={props}/>
Now the props in EventCalender will contain a key called as props which wil lhave your data but you are trying to access it directly on props which is why it is undefined.
The correct way to pass the props here would be to use spread syntax like
<EventCalendar {...props}/>
I can not understand how to deal with new typings of Redux.
Right now i am trying to complete this tutorial https://maxfarseer.gitbooks.io/redux-course-ru/content/index.html
At this moment I am on "Creating Store" phase with this simple code below:
store/configureStore.ts
import {createStore, Reducer} from 'redux';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState);
if (module.hot) {
module.hot.accept('../reducers', () => {
const nextRootReducer=require('../reducers');
store.replaceReducer(nextRootReducer);
})
}
return store;
}
When I try to compile it messages me about this error:
ERROR in ./src/store/configureStore.ts
(10,34): error TS2345: Argument of type '{}' is not assignable to parameter of type '<A extends Action>(state: any, action: A) => any'.
Type '{}' provides no match for the signature '<A extends Action>(state: any, action: A): any'
This is my index file of reducers
import {combineReducers} from "redux/index";
import page from './page';
import user from './user';
const rootReducer = combineReducers({
page,
user
});
export default rootReducer;
//page.ts
const initialState = {
year: 2016,
photos: []
};
function page(state = initialState,
action = {type: '', payload: null}) {
return state
}
export default page;
//user.ts
const initialState = {
name: 'Аноним'
};
export default function user(state = initialState,
action = {type: '', payload: null}) {
return state
}