Cart Items are not showing in cart after app restart. I think its due to empty array cartItems.
import {AsyncStorage} from 'react-native'
const cartItems = [] //
AsyncStorage.getItem("cartItems").then(res=>
res!=null?res:[])
const initState = { cart: { items: cartItems } };
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
rootReducers,
initState,
composeEnhancer(applyMiddleware(thunk))
);
export default store;
It seems like this.props.cartItems is undefined which is why when you attempt to use Array.prototype.slice on them, it fails.
The issue seems to origin in the mapStateToProps, you're referencing state.cart.items but in your reducer, the structure seems to be state.items.
This should therefore work:
const mapStateToProps = (state) => ({
products: state.products.filteredItems,
cartItems: state.items,
});
Related
I'm trying to build a react native application using expo, firebase, and redux toolkit. With redux toolkit, I have created a slice that has an asyncThunk to get the data, such as a username, from firestore and I have an extra reducer that sets that data to the store.
In my screen file I'm using useSelector() to get the store value, and when I log the value I can see the store is changing but the screen isn't re-rendering. Also, I have a authentication slice in my store as well that works correctly but I don't know if that would mess anything up for this.
Store code:
export const store = configureStore({
reducer: {
firestore: firestoreSlice,
userAuth: authSlice,
},
})
Slice Code is below:
const initialState = {
isLoaded: false,
username: null
}
export const getUsername = createAsyncThunk('firestore/getUsername', async (userId) => {
const firestore = getFirestore();
const docRef = doc(firestore, "users", userId);
const docSnap = await getDoc(docRef);
const data = {
username: docSnap.data().username,
isLoaded: true
}
return data;
})
const firestoreSlice = createSlice({
name: 'firestore',
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(getUsername.fulfilled, (state,action) => {
//state.isLoaded = action.payload.isLoaded;
//state.username = action.payload.username;
return Object.assign({}, state, {username: action.payload.username, isLoaded: action.payload.isLoaded})
})
}
});
export const selectUsername = (state) => state.firestore.username;
export const selectIsLoaded = (state) => state.firestore.isLoaded;
export default firestoreSlice.reducer;
Code inside Home Screen component below:
const username = useSelector(selectUsername);
const isLoaded = useSelector(selectIsLoaded);
useEffect(() => {
dispatch(getUsername(auth.currentUser.uid));
console.log(username + "..." + isLoaded);
}, [username]);
return(
<View style={styles.container}>
<StatusBar></StatusBar>
<Text>Home</Text>
{isLoaded == false ? (
<Text>Welcome, set your username in profile page</Text>
) : (
<Text>Welcome, {username}</Text>
)}
</View>
);
Console Log:
null...false
testing...true
In the console log, I can see the updated username and isLoaded values since UseEffect runs on changes to the username variable. However, the screen isn't re-rendering and I don't see the username displayed. I know that useSelector() only re-renders on reference changes to state, which is why in the extra reducer I tried mutating the state and creating a new object but neither caused the re-render.
Could someone help? Been stuck on this for like a week now!
Thanks!
I'm new in react native world and i'm on a new project with store (manage by redux).
I encounter an issue with custom hooks and useEffect
here my custom hooks
const useTheme = () => {
const [activeTheme, setActiveTheme] = useState();
const { id: universID, defaultTheme: universDefaultTheme } = useSelector(
(state) => state.univers
);
const { theme } = useSelector((state) => state);
const { themes: activeThemes } = useSelector((state) => state.settings);
const dispatch = useDispatch();
//set theme when univers change
useEffect(() => {
console.log('TODO TOO MANY CALLS!!!!!', universID);
if (universID) {
setTheme(
activeThemes.find((theme) => theme.univers === universID)?.theme
);
}
}, [universID]);
//get active theme of current univers
useEffect(() => {
setActiveTheme(
activeThemes.find((theme) => theme.univers === universID)?.theme
);
}, [activeThemes]);
... rest of code ...
return {
theme,
activeTheme,
setTheme,
};
}
on components i use
const {
theme: { colors },
} = useTheme();
My issue is that on every import the useEffect(()=>{},[universID]) is trigger. UniversID come from redux store.
If i understand clearly when i import useTheme() the reference of universID change because there are copy of universID from store created, and reference change.
if i pass universID as arguments to useTheme hooks there are no problem, cause reference is the same. But if i do this i need tu make a useSelector(universID) on every components who import useTheme hooks.
My understanding of mecanism is good ?
There are a way to get universID from store with the same reference on every import, for not trigger useEffect(,[universID]) on every import ? without pass universID as arguments of useTheme (i.e. useRef, useCallback) ?
Thanks for the time past to read (or better, to answer ;))
I am trying to dynamically update the API path in my Vuex state. Vuex must have a default path "example.com/api/datasetA.json" set when the page loaded and I want to update the path to "example.com/api/datasetB.json" by the user interaction and fetch the new API data immediately.
The relevant part of my code is as follows (updated code):
VUEX:
export const state = () => ({
apiData: [],
apiId: 'datasetA.json'
});
export const mutations = {
fillApiData: (state, data) => {state.apiData = data},
updateApi: (state, newApiId) => {state.apiId = newApiId;}
};
export const actions = {
async getApiData({commit, state}) {
const response = await this.$axios.$get('https://example/api/'+state.apiId);
commit('fillApiData', response);
then VUE method as follows:
methods: {
updateApi(apiId) {
this.$store.commit('updateApi', apiId)
}
Create a mutation that changes the vuex state. Then run this mutation(commit) in the getApiData function
export const state = () => ({
apiData: [],
apiId: 'datasetA.json'
});
export const mutations = {
updateAPI(state, newApiId ) {
state.apiId = newApiId;
}
};
export const actions = {
async getApiData({commit, state}) {
const response = await this.$axios.$get('https://example/api/'+state.apiId);
commit('updateValue', response);
commit('updateAPI', 'some.new.datasetB.json');
}
}
I can update the state directly by using this.$store.state.apiId = apiId in methods but I know this is bad practice
You are correct. However, if you would like that approach to update the state outside Vuex, you can use mutations to change the Vuex - This is good practice.
Then you can do
this.$store.commit('updateAPI', 'my new value')
I have been handed a project and been told to use React Hooks instead of Redux as much as possible. Is it possible to replace the reducers and thunks below with React Hooks? Is it worth it to replace?
Reducers/index.js
import { combineReducers } from 'redux'
import {createActions, createReducer, Types as ReduxSauceTypes} from 'reduxsauce'
import { reducer as ProfileReducer } from '#Reducers/Profile'
const appReducer = combineReducers({
profile: ProfileReducer,
// other reducers
})
const { Types, Creators: Actions } = createActions({
resetApp: []
})
const rootReducer = createReducer([], {
[Types.RESET_APP]: (state, action) => {
return appReducer(undefined, action)
//Passing undefined as state will make all the reducers using their initial states.
},
[ReduxSauceTypes.DEFAULT]: (state, action) => {
return appReducer(state, action)
}
})
const resetReduxStore = () => {
return dispatch => {
dispatch(Actions.resetApp())
}
}
export { rootReducer, resetReduxStore }
Reducers/Profile/index.js
import { createActions, createReducer } from 'reduxsauce';
export { default as thunks } from './thunks.js';
/* ------------- Initial State ------------- */
export const INITIAL_STATE = {
user: {},
};
/* ------------- Types and Action Creators ------------- */
export const { Types, Creators } = createActions({
setUser : ['user'],
});
/* ------------- Hookup Reducers To Types ------------- */
export const reducer = createReducer(INITIAL_STATE, {
[Types.SET_USER]: (state, { user }) => {
return {
...state,
user
};
},
});
export default Creators;
"Is it worth it?" is a matter of opinion, but I'll give mine.
Re: useReducer
React now has a useReducer hook that allows you to update a state by dispatching actions. You can use the same reducers that you have already. By default, useReducer only manages the state for the component that it's in. It doesn't come with a context provider. You could create your own context but at that point you are re-creating Redux.
My opinion: Use the React useReducer hook if you have state that it localized to one part of your app. Keep the reducer in some component and pass down callbacks and values via props. If you have state that is global in nature then use Redux.
Re: useState
You asked if you can replace your reducers. As I said, the useReducer hook would use the same sort of reducer so you wouldn't be replacing it. Perhaps the question you need to be asking is do I need a reducer-like system to update this state?
My Opinion: The code that you've posted here is extremely simple and it would be a good candidate for a basic useState hook.
const [user, setUser] = useState();
You could combine this with a context provider and a useContext hook to have a global profile state. If you start having multiple contexts for multiple states -- that's when you want to use Redux instead.
const UserContext = React.createContext([
undefined, // user
() => {} // setUser
]);
// takes no `value` because the state is internal
export const UserProvider = ({children}) => {
const [user, setUser] = React.useState<MaybeUser>();
return (
<UserContext.Provider value={[user, setUser]}>
{children}
</UserContext.Provider>
)
}
export const useUser = () => useContext(UserContext);
Usage in some component:
const Test = () => {
const [user, setUser] = useUser();
...
}
Re: Thunks
Thunks are function of dispatch so they can easily be rewritten with the useDispatch hook, if using Redux. You can use the useSelector hook instead of the getState() argument of a thunk.
There are lots of ways to replace thunks using hooks. But the only thunk that you have here is your resetReduxStore which really doesn't need to be a thunk.
I'm trying to access a redux store with redux persist in a service for my react native app.
I need a specific token to set a websocket connection.
My code so far:
./redux/Store.js:
const persistedReducer = combineReducers({
tokens: persistReducer(secureConfig, TokensReducer),
});
const store = createStore(persistedReducer);
const configureStore = () => {
const persistor = persistStore(store);
return { persistor, store };
};
export default configureStore;
./redux/reducers/TokenReducer
const initialState = {
accessToken: null,
refreshToken: null
}
const TokensReducer = (state = initialState, action) {
// reducer
};
export default TokensReducer;
./service/websocket.js
import configureStore from '../redux/Store';
const { store } = configureStore();
console.log(store.getState().tokens);
The problem is, I'm not getting the persisted content, but I'm getting the initial state (accessToken = null, refreshToken = null).
When I access the store from inside my app (inside components inside and ), I get the correct values.
Edit:
when I wrap the console.log in a setTimeout() of let's say 1 second, it works! So it asynchronous, but how can I create my code to wait for it and not using setTimeout?