Functions such as add and remove from cart works fine. But the data is not saved successfully after the browser is refreshed. How to keep the data in the cart even when the browser is refreshed.
here is my code. if there is any code you would like to see let me know
import React, { useEffect } from "react";
import { useStateValue } from "../hooks/StateProvider";
function CheckoutPage() {
const [{ basket }, dispatch] = useStateValue();
useEffect(() => {
window.localStorage.setItem("basket", JSON.stringify(basket));
}, [basket]);
useEffect(() => {
const basketData = window.localStorage.getItem("basket");
if (basketData !== null) dispatch(JSON.parse(basketData));
}, []);
import React, { createContext, useContext, useReducer } from "react";
export const StateContext = createContext();
export const StateProvider = ({ reducer, initialState, children }) => (
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
);
export const useStateValue = () => useContext(StateContext);
You have to use Web Api for that . sessionStorage , localStorage , Indexed DB or etc..
Related
I have a weird probleme here, so i am working with redux and this is my component code for a selected product from reducer state.
import './productInfo.css';
import { useParams } from 'react-router-dom';
import { getProduct } from '../../actions/productActions';
import { connect } from 'react-redux';
import { useEffect, useState } from 'react';
const ProductInfo = (props) => {
const [usedProduct, setUsedProduct] = useState(props)
const {id} = useParams();
useEffect(() => {
props.getProduct(id);
}, [usedProduct])
let newUsedProduct = usedProduct;
console.log(usedProduct.products[0].name)
return (
<div>
</div>
);
}
const mapStateToProps = (state) => {
return{
products : state.myProduct.product
}
}
export default connect( mapStateToProps, { getProduct })(ProductInfo);
so when i launch the component for the first time it works and i got the necessary data from the reducer state but when i refresh the page it gives me this error :
Uncaught TypeError: Cannot read properties of undefined (reading '0')
in this line of code :
console.log(usedProduct.products[0].name)
i'm totally lost !
When you reload the page all React state, including the Redux state, are lost since they exist only in memory. The component should handle potentially missing data.
Example:
const ProductInfo = ({ getProduct, products }) => {
const { id } = useParams();
useEffect(() => {
getProduct(id);
}, [id, getProduct]);
useEffect(() => {
console.log(products?.[0]?.name); // <-- log in effect
}, [products]);
return (
<div>
...
</div>
);
};
const mapStateToProps = (state) => ({
products: state.myProduct.product || [], // <-- provide fallback
});
export default connect(mapStateToProps, { getProduct })(ProductInfo);
I'd also suggest updating the code to use the react-redux hooks.
Example:
import { useDispatch, useSelector } from 'react-redux';
const ProductInfo = () => {
const dispatch = useDispatch();
const products = useSelector(state => state.myProduct.product || []);
const { id } = useParams();
useEffect(() => {
dispatch(getProduct(id));
}, [id, getProduct]);
useEffect(() => {
console.log(products?.[0]?.name); // <-- log in effect
}, [products]);
return (
<div>
...
</div>
);
};
export default ProductInfo;
Redux store (in default setup) will not persist through refresh, you need to use some persist method (store it in localstorage or something like that) or you need always to repopulate store before reading it.
I'm working on a personal React project with Redux and I've an issue. All my redux functions are working well except one. I want to load all the requests into the redux. In requestAction.js, I have the first console.log but not the second from the return function(dispatch). Have you any idea why ?
Thank you in advance =)
Here is my code :
import React, {useState, useEffect} from "react"
import {connect} from "react-redux"
import {getAllRequests} from "../../api/requests"
import {loadAllRequests} from "../../actions/request/requestAction"
import {convertDate} from "../../utils/utils"
import Header from "../headers/header"
import HeaderPages from "../headers/headerPages"
import Footer from "../footer"
import AdminMenu from "../../components/adminMenu"
const AdminRequests = (props) => {
const headerTitle ="Administration"
const headerBreadcrumbs = [{value: "Accueil", link:"/"},{value: "Administration", link:"/admin"},{value: "Commandes", link:null}]
const [displayedRequests, setDisplayedRequests] = useState([])
useEffect(() => {
loadDatas()
}, [])
useEffect(() => {
loadDisplayedRequests(props.requests.list)
}, [props.requests])
const loadDatas = () => {
getAllRequests()
.then(requestsDB => {
loadAllRequests(requestsDB) //My redux function
})
}
//Construction de la liste des commandes à afficher
const loadDisplayedRequests = (requests) => {
requests.map((requestItem) => {
setDisplayedRequests(displayedRequests => [...displayedRequests,
<article key={requestItem.id} className="profile-user-request-item">
<section className="request-item-header">
<p>N°{requestItem.request_number}</p>
<p>du {convertDate(requestItem.request_date)}</p>
<p>Statut : {requestItem.preparation_status}</p>
</section>
<section className="request-item-resume">
<p>Total</p>
<p>{requestItem.total_amount} € TCC</p>
</section>
</article>])
})
}
const showDisplayedRequests = () => {
return(
<section>
{displayedRequests}
</section>
)
}
return (
<div className="root">
<Header />
<HeaderPages headerTitle={headerTitle} headerBreadcrumbs={headerBreadcrumbs}/>
<section className="admin-container">
<AdminMenu />
<section className="admin-content">
<h4>Gestion des commandes</h4>
{showDisplayedRequests()}
</section>
</section>
<Footer />
</div>
)
}
const mapStateToProps = (store) => {
return {
requests: store.requests
}
}
const mapDispatchToProps = {
loadAllRequests
}
export default connect(mapStateToProps, mapDispatchToProps)(AdminRequests)
requestAction.js
import {LOAD_ALL_REQUESTS} from "./action-type"
export const loadAllRequests = (requests) => {
console.log("requests action = ", requests) //Displayed
return function(dispatch){
console.log("dispatch") //Not displayed
dispatch({
type: LOAD_ALL_REQUESTS,
payload: requests
})
}
requestReducer.js
import { LOAD_ALL_REQUESTS } from "../actions/request/action-type"
const initialState = {
list: []
}
export default function RequestReducer(state = initialState, action) {
switch(action.type){
case LOAD_ALL_REQUESTS :
return {list: action.payload}
break
default :
return state
break
}
}
index.js
import {combineReducers } from "redux"
import UserReducer from "./userReducer"
import ProductsReducer from "./productsReducer"
import RequestReducer from "./requestReducer"
const rootReducer = combineReducers({
user: UserReducer,
products: ProductsReducer,
requests: RequestReducer
})
export default rootReducer
The problem at heart is that you don't dispatch. You need to
const dispatch = useDispatch()
useEffect(() => {
dispatch(loadDisplayedRequests(props.requests.list))
// instead of
// loadDisplayedRequests(props.requests.list)
}, [props.requests])
Adding to that: What you have written there is not an action creator, but a thunk. It will only work if you have the thunk middleware enabled - and even then, for this simple use case it just does nothing extra that you need.
As a normal action creator, it would look like this:
import {LOAD_ALL_REQUESTS} from "./action-type"
export const loadAllRequests = (requests) => {
return {
type: LOAD_ALL_REQUESTS,
payload: requests
}
}
Generally, I want to make you aware that you are writing a very old style of Redux here and might have been following an outdated tutorial. Modern Redux does not have string action type constants, switch case reducers, action creators or connect any more.
For a quick look at modern Redux, take a look at https://redux.js.org/tutorials/fundamentals/part-8-modern-redux and for a longer tutorial, read https://redux.js.org/tutorials/essentials/part-1-overview-concepts
Hi im new to redux and im trying to create a movie app using the API from www.themoviedb.org. I am trying to display the popular movies and im sure the API link works since ive tested it in postman but i cant seem to figure out why redux doesnt pick up the data.
//action
import { FETCH_POPULAR } from "./types";
import axios from "axios";
export const fetchPopularMovies = () => (dispatch) => {
axios
.get(
`https://api.themoviedb.org/3/movie/popular?api_key=${API}&language=en-US`
)
.then((response) =>
dispatch({
type: FETCH_POPULAR,
payload: response.data
})
)
.catch((err) => console.log(err));
};
//reducer
import { FETCH_POPULAR } from "../actions/types";
const initialState = {
popular: [],
};
export default function (state = initialState, action) {
switch (action.type) {
case FETCH_POPULAR:
return {
...state,
popular: action.payload,
};
default:
return state;
}
}
import React from "react";
import { connect } from "react-redux";
import Popular from "./Popular";
const FetchedPopular = (props) => {
const { popular } = props;
let content = "";
content =
popular.length > 0
? popular.map((item, index) => (
<Popular key={index} popular={item} />
))
: null;
return <div className="fetched-movies">{content}</div>;
};
const mapStateToProps = (state) => ({
popular: state.popular.popular,
});
export default connect(mapStateToProps)(FetchedPopular);
import React from "react";
import "../Styles.css";
const Popular = (props) => {
return (
<div className="movie-container">
<img
className="poster"
src={`https://image.tmdb.org/t/p/w400/${props.poster_path}`}
/>
</div>
);
};
export default Popular;
I cant really tell what I'm missing can someone help?
Next to mapStateToProps you need to create mapDispatchToProps. After that, you will be able to call your Redux action from your React component.
I suggest you the mapDispatchToProps as an Object form. Then you need to use this mapDispatchToProps as the second parameter of your connect method.
When you will have your action mapped to your component, you need to call it somewhere. It is recommended to do it for example on a component mount. As your React components are Functional components, you need to do it in React useEffect hook.
import React, { useEffect } from "react";
import { connect } from "react-redux";
import Popular from "./Popular";
import { fetchPopularMovies } from 'path_to_your_actions_file'
const FetchedPopular = (props) => {
const { popular } = props;
let content = "";
useEffect(()=> {
// call your mapped action (here it is called once on component mount due the empty dependency array of useEffect hook)
props.fetchPopularMovies();
}, [])
content =
popular.length > 0
? popular.map((item, index) => (
<Popular key={index} popular={item} />
))
: null;
return <div className="fetched-movies">{content}</div>;
};
const mapStateToProps = (state) => ({
popular: state.popular.popular,
});
// create mapDispatchToProps
const mapDispatchToProps = {
fetchPopularMovies
}
// use mapDispatchToProps as the second parameter of your `connect` method.
export default connect(mapStateToProps, mapDispatchToProps)(FetchedPopular);
Moreover, as I wrote above in my comment, your Popular does not have the prop poster_path but it has the prop popular which probably has the property poster_path.
import React from "react";
import "../Styles.css";
const Popular = (props) => {
return (
<div className="movie-container">
<img
className="poster"
src={`https://image.tmdb.org/t/p/w400/${props.popular.poster_path}`}
/>
</div>
);
};
export default Popular;
When a user log to a react app, I fill data to authState object. Inside the app I fill other state objects with data. I want to clear all those states when the user logout
for example I have this provider
import { createContext, useEffect, useReducer } from "react";
import auth from "./reducers/auth";
import pendiente from "./reducers/pendiente";
import historico from "./reducers/historico";
import authInitialState from "./initialStates/authInitialState";
import pendienteInitialState from "./initialStates/pendienteInitialState";
import historicoInitialState from "./initialStates/historicoInitialState";
export const GlobalContext = createContext();
export const GlobalProvider = ({ children }) => {
const [authState, authDispatch] = useReducer(auth, [], () => {
const localData = localStorage.auth;
return localData ? JSON.parse(localData): authInitialState;
});
const [pendienteState, pendienteDispatch] = useReducer(
pendiente,
pendienteInitialState
);
const [historicoState, historicoDispatch] = useReducer(
historico,
historicoInitialState
);
useEffect(() => {
localStorage.auth = JSON.stringify(authState);
}, [authState]);
return (
<GlobalContext.Provider
value={{
authState,
authDispatch,
pendienteState,
pendienteDispatch,
historicoState,
historicoDispatch,
}}
>
{children}
</GlobalContext.Provider>
);
};
In Logout function I'm sending and action (logout) with 3 dispatchs.
const {
authState,
authDispatch,
pendienteDispatch,
historicoDispatch,
} = useContext(GlobalContext);
const handleLogout = () => {
logout(history)(authDispatch, pendienteDispatch, historicoDispatch);
};
Inside the action I send a dispatch an to every sate objcet to clear the data with it's initial state
This works fine, but I think this is not the correct way to do it
const logout = (history) => (
dispatch,
pendienteDispatch,
historicoDispatch
) => {
localStorage.removeItem("token");
dispatch({ type: LOGOUT_USER });
pendienteDispatch({ type: CLEAR_PENDIENTE_DATA });
historicoDispatch({ type: CLEAR_HISTORICO_DATA });
history.push("/");
};
¿Any ideas ?
import { createContext } from 'react';
import axios from 'axios'
import keys from '../../keys'
const getCollection = () => {
console.log("getCollectionCalled")
return axios.get(`${keys.url}/collection`)
.then(data => {
console.log(data)
return data
})
.catch(err => {
return {}
})
}
const SHOP_DATA = getCollection();
const CollectionsContext = createContext(SHOP_DATA);
export default CollectionsContext;
This is the code I am trying to run but context is not setting as per the data from the server, when I am using this in other component like this
const collections = useContext(CollectionsContext);
console.log("COLLECTIONS ", collections)
It is consoling it as :
COLLECTIONS Promise {<pending>}__proto__:
Promise[[PromiseStatus]]: "resolved"
[[PromiseValue]]: undefined
Kindly rectify me I am unable to think how may I implement it.
In this case you're returning a promise as initial context value, what you need to do is something between these lines:
import React, { useState, useEffect, createContext, useContext } from 'react'
import axios from 'axios'
import keys from '../../keys'
const INITIAL_STATE = {}
const CollectionsContext = createContext(INITIAL_STATE)
const Provider = ({ children }) => {
const [collection, setCollection] = useState()
useEffect(() => {
axios.get(`${keys.url}/collection`)
.then(data => setCollection(data))
.catch(() => setCollection(INITIAL_STATE))
}, [])
return (
<CollectionsContext.Provider value={collection}>
{children}
</CollectionsContext.Provider>
)
}
export const useCollection = () => useContext(CollectionsContext)
export default Provider
Then you wrap the top level (as high as it's needed, not neccessarily the highest) with the provider:
import CollectionsProvider from '.../.../somewhere'
<CollectionsProvider>...rest of components...</CollectionsProvider>
In this case, INITIAL_STATE is the value provided until value is undefined or you haven't used Provider. For example, you use useCollection outside of CollectionsProvider.