I created my themeSlice with hardCoded themes in mind initialState:{theme: "lightTheme"}. I quickly realized a better way to do it was by using a simple boolean value initialState: { darkTheme: false }.
After making the changes and running the program again, I still see in console "theme": "lightTheme"
I am using React-toolkit and RTK-Query in this project, not sure if my setup is the cause.
This is my Store setup:
const rootReducer = combineReducers({
test: testSlice,
theme: darkThemeSlice,
[apiSlice.reducerPath]: apiSlice.reducer,
});
const persistConfig = {
key: "root",
storage: AsyncStorage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = configureStore({
reducer: persistedReducer,
devTools: true,
middleware: (getDefaultMiddleWare) =>
getDefaultMiddleWare({ serializableCheck: false }).concat(
apiSlice.middleware
),
});
export default store;
I cleared cache and re-ran my app, and nothing changed.
Any idea what is happening here?
You are using redux-persist. initialState is "the state if there is no state already - but in your case there is always the state from when you were on that website before.
As such, initialState in your case only takes place when a user visits the website for the first time, ever.
If you are still in development, you can just use your browser devtools to reset local storage. If this is deployed somewhere, you cannot go into all your users' browsers, so you will have to create a migration in redux-persist to move people from your first initial state to your second initial state. (Especially necessary if your state changes structure!)
to clear wrong and hard set values in initial state
you can try
const persistConfig = {
key: "root",
storage: AsyncStorage,
blacklist: ["theme"],
};
and once cleared you can remove the blacklist item.
Related
I have a React app where I've used the #rtk-query/codegen-openapi tool to generate my authentication queries and mutations. I'm also using Redux Persist to store some of this data.
I'm trying to figure out a way to invalidate this data programmatically when the user logs out. Is it possible to do this?
My redux store configuration looks something like this:
const persistConfig = {
key: "root",
storage,
blacklist: ["api"],
};
const authPersistConfig = {
key: "auth",
storage,
whitelist: [],
};
const rootReducer = combineReducers({
settings: settingsSlice.reducer,
[applicationApi.reducerPath]: applicationApi.reducer,
[enhancedAuthenticateApi.reducerPath]: persistReducer(
authPersistConfig,
enhancedAuthenticateApi.reducer
),
});
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const buildStore = () =>
configureStore({
reducer: persistedReducer,
middleware: getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}).concat([enhancedAuthenticateApi.middleware]),
});
I'm pretty new to Redux and RTK Queries so any help is appreciated.
I tried everything like spread operator but nothing works.
Here is my reducer
//state is an array of objects.
const initialState = [
{taskName: "kkkkk", isEdit: false},
]
export const todoReducer = (state=initialState, action) =>{
switch(action.type){
case 'add' :
const temp=
{
taskName: action.payload.taskName,
isEdit: action.payload.isEdit
}
state.push(temp);
return {state}
default: return state
}
}
The error message indicates that you are using Redux Toolkit - that is very good. The problem is that you are not using createSlice or createReducer and outside of those, in Redux you are never allowed to assign something to old state properties with = or call something like .push as it would modify the existing state.
Use createSlice instead:
const initialState = [
{taskName: "kkkkk", isEdit: false},
]
const slice = createSlice({
name: 'todos',
reducers: {
add(state, action) {
state.push(action.payload)
}
}
})
export const todoReducer = slice.reducer;
// this exports the auto-generated `add` action creator.
export const { add } = slice.actions;
Since the tutorial you are currently following seems to be incorporating both modern and completely outdated practices, I would highly recommend you to read the official Redux Tutorial instead, which shows modern concepts.
I am having my issue with my React app, it's a simple form that allows user to upload their images then it will be shown on screen, now when I upload an image what happens is all the images will become one which is the most recent uploaded one.
the way the app is set up: I have a full crud operation on a react app that stores data in state and saves it in mongodb, I added a url key in the state object, with cloudinary I added another state to store the image uploaded.
I am trying to see if I can update the state url key value with the value from cloudinary so I would only get the most recent uploaded image.
export default function App() {
const [state, setState] = useState({
tracks: [{ track:"",artist:"", album:"", year:"", url:""}],
newTrack:{
track:"",
artist:"",
album:"",
year:"",
url:"",
},
editMode: false
});
const [userState, setUserState] = useState({
user: null
})
const [image, setImage] = useState('')
const FormPage = (props) => {
const [imageSelected, setImageSelected] = useState('');
const uploadImage = () => {
const formData = new FormData()
formData.append('file', imageSelected)
formData.append('upload_preset', 'musicimages')
Axios.post(
' https://api.cloudinary.com/v1_1/dklcmfo0q/image/upload', formData)
.then((res) => {
props.setImage(res.data.url);
// console.log(res.data.url)
})
};
Would you please try by removing "props" for this line?
props.setImage(res.data.url);
Thanks,
Mo
Solved. passed the image url from the image state to the handleCHange function as an extra parameter that would then set state with the image url as a string just like I had in the backend mongodb schema. it gets stored in mongodb with an id so it would match the form input details.
kinda proud, it took me three days lol
What will happen when i use Redux Toolkit Query with redux-persist?
Will it use the persisted state or will the state be refetched?
There is an official guide for this now. At the time of writing this, you could configure it this way:
// store.ts
import { configureStore, combineReducers } from '#reduxjs/toolkit'
import {
persistStore,
persistReducer,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
} from 'redux-persist'
import AsyncStorage from '#react-native-async-storage/async-storage'
import someSlice from 'config/features/someSlice'
import { api } from 'config/services/api'
const reducers = combineReducers({
someSlice,
[api.reducerPath]: api.reducer,
})
const persistConfig = {
key: 'root',
version: 1,
storage: AsyncStorage,
}
const persistedReducer = persistReducer(persistConfig, reducers)
export const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
// Redux persist
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}).concat(
api.middleware,
),
})
export let persistor = persistStore(store)
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
// App.tsx for native or index.tsx
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>,
I just really wouldn't do it.
That restored data could be all kinds of stale and when a user hits F5 they usually expect data to be up-to-date, not a week old or something. Also, while the store slice is restored, information about subscriptions might be problematic (because the "subscribing components" never exist, they can also never unmount and thus get never cleaned from the store).
So, I'd blacklist the api slice from being persisted.
If you want that stuff to be cached, do it with cache headers in your server. The browser will do all the caching for you, but also allow the user to clear the cache or force a refetch with ctrl+shift+r - so the browser would just behave more than the user expects.
Use extractRehydrationInfo like this:
import { REHYDRATE } from 'redux-persist'
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
extractRehydrationInfo(action, { reducerPath }) {
if (action.type === REHYDRATE) {
return action.payload[reducerPath]
}
...
},
Full official guide here.
Using Redux Toolkit, I'm trying to dispatch an action without context of event or etc., so I get the following error:
error TS2554: Expected 2 arguments, but got 1. An argument for 'action' was not provided.
With following code:
const App = () => {
const dispatch = useDispatch();
useEffect(() => {
(async () => {
const result = await fetchConfig();
dispatch(setConfig({ ConfigReducerState: result })); // ---> Error is here <---
})();
}, [dispatch]);
};
The reducer:
export const configSlice = createSlice({
name: 'config',
initialState,
reducers: {
setConfig(state, action) {
const { server, map } = action.payload;
state.server = server;
state.map = map;
},
},
});
Usually I give one parameter to action creator functions - object representing the payload, no need to refer the state. But here I can't. What am I doing wrong?
I've seen this before and every time, it was... a bug in IntelliJ/WebStorm.
See https://youtrack.jetbrains.com/issue/WEB-46527 and https://youtrack.jetbrains.com/issue/WEB-42559 - essentially, WebStorm has their own "quick TypeScript interpolation that does not use the official tsserver for type checking, but something self-cooked, that guesses types just based on things having similar names - and regularly gets things wrong.
If I understand their system correctly, you should be able to see the correct types by hovering over while holding Ctrl down.
In the end, I can't really tell you how to fix this other than switching to an IDE that does not randomly guess, but actually uses TypeScript to evaluate TypeScript types.
How did you import setConfig? I had the same issue and it turned out that by mistake I used
import setConfig from './configSlice'
instead of
import { setConfig } from './configSlice'
It was importing the default export (whole slice reducer, aliased as setConfig) instead of just this one function...