React Context not updating value to pass to another page - reactjs

I am creating an ecommerce app with Nextjs and want to share data between pages. I know that we can't use props to pass data between the pages and so was looking into react context api. This is my first time using react context api. I've researched and found that you should add the Provider in the _app.js page in nextjs.
But this shares the data among all the pages. Plus my data is being retrieved by getStaticProps in the slug page of the app. I want to get this data into the checkout page of my app.
This is the context I have created:
import { createContext, useState, useContext } from 'react';
const productContext = createContext({} as any);
export const ProductProvider = ({ children }) => {
const [productData, setProductData] = useState('');
return <productontext.Provider value={{ productData, setProductData }}>{children}</productContext.Provider>;
};
export const useProduct = () => useContext(productContext);
_app.js
import { ReportProvider } from '../contexts/ReportContext';
export default function CustomApp({ Component, pageProps }) {
return (
<ReportProvider>
<Component {...pageProps} />
</ReportProvider>
);
}
I import this into the slug page and try to update the state from here
// [slug].js
import client from '../../client'
import {useProduct} from './productContext';
const Post = (props) => {
const {setProductData} = useProduct();
const { title = 'Missing title', name = 'Missing name' , price} = props
setProductData(title);
return (
<article>
<h1>{title}</h1>
<span>By {name}</span>
<button>
Buy Now
</button>
</article>
)
}
Post.getInitialProps = async function(context) {
const { slug = "" } = context.query
return await client.fetch(`
*[_type == "post" && slug.current == $slug][0]{title, "name": author->name, price}
`, { slug })
}
export default Post
However this productData is not accessible from another page and the react context state is not getting updated.
Any idea why this could be happening?

Once you've updated your context value. Please make sure you are using next/link to navigate between pages. Here is details about next/link

Related

sending data with context api to components that have parameter in their route

sorry about the bad question title:(
im trying to fetch data and send it to other components by context api.
but i have a problem with routes that have parameters in their path
for example when im trying to access the data in "MoviePage" component that its route is
<Route path="movie/:id" element={<MoviePage />} />
i cant access to the data in this component and returns error bad request
this is the context file:
import React, { useState, useEffect } from 'react'
import axios from 'axios'
const API_URL = './JSON/data.json';
export const MainContext = React.createContext({id: 1})
const ContextData = ({ children }) => {
const [data, setData] = useState([])
useEffect(() => {
axios.get(API_URL)
.then(res => setData(res.data))
}, [])
return(
<MainContext.Provider value={ data }>
{ data && data.length && children }
</MainContext.Provider>
)
}
export default ContextData;
but as i said i have no problem with other compoents and pages that have not parameters in their routes

How to call getServerSideProps() in nested pages in Next.js?

I have 2 pages (parent and child) with dynamic routes, and I want to call getServerSideProps() in the child only.
The first page, [Post].js:
import React from 'react'
import PropTypes from 'prop-types'
import axios from 'axios'
import Index from './index'
const Post = ({ children, making }) => {
return (
<Index>
{making
? (
<div>
<p>Making ID is: {making.id}</p>
{children}
</div>
)
: (<p>Item not found</p>)}
</Index>
)
}
Post.propTypes = {
children: PropTypes.node,
making: PropTypes.object,
}
export const getServerSideProps = async ({ query: { makingID } }) => {
const url = `http://localhost:1337/makings/${makingID}`
const res = await axios.get(url)
const making = res.data
return { props: { making } }
}
export default Post
The second page (the parent), [tabs].js:
import React from 'react'
import { useRouter } from 'next/router'
import Post from '../[Post]'
const tabs = () => {
const router = useRouter()
const { tabs } = router.query
return (
<Post>
hello {tabs}
</Post>
)
}
export default tabs
But if I use Post as a component, then getServerSideProps() does not work. I could use the function in tabs, but I want it in the child only.
getServerSideProps function can be used only in a page. You can't use it in components. Instead, you can pass fetched data as props to page components.
Also, pages can't be nested. If pages share same components, then you can include these components in both pages.

Should we render react component at multiple places?

So, i have component called Balance component which i want to display in navbar and on a page (summary page) component as well.
But i want to prevent it from making an api request twice.
The code of balanceSlice:
import {createSlice, createAsyncThunk} from '#reduxjs/toolkit';
export const updateBalance = createAsyncThunk(
'balance/fetchBalance',
async () => {
const response = await fetchBalance();
return response.data;
}
);
const initialState = {
value: 0
};
export const balanceSlice = createSlice({
name: 'balance',
initialState,
reducers: {
},
extraReducers: () => {...}
});
export const selectBalance = (state) => state.balance.value;
export default balanceSlice.reducer;
Here is the code of Balance
import {useState, useEffect} from 'react';
import {useSelector, useDispatch} from 'react-redux';
import {updateBalance, selectBalance} from './balanceSlice';
function Balance() {
const balance = useSelector(selectBalance);
const dispatch = useDispatch();
useEffect(() => {
dispatch(updateBalance());
console.log('component ran')
}, [dispatch]);
return (
<>
{balance}
</>
);
}
export default Balance;
As i want to reuse this component at multiple places, so i haven't added any styling in this component. The styling is being done in summary page and the navbar. The updateBalance is requesting new balance from the api. I am using redux toolkit and I am trying to follow single responsibility principle and making component small, reusable and testable.
Is it recommended to render balance component at multiple places?
Navbar code:
import Balance from './../../features/balance/Balance';
function TopNavbar() {
return (
<Navbar>
<span className={styles.navbar__text__span}><Balance /></span>
</Navbar>
);
}
Similarly, i am importing Balance component in summaryPage component and rendering it. Is it a recommended approach or should i simply import selectBalance from balanceSlice and display it in navbar?
You can render Balance components wherever you need.
Balance component would look like this.
function Balance() {
const balance = useSelector(selectBalance);
const dispatch = useDispatch();
useEffect(() => {
if (balance.status === 'ready') {
dispatch(updateBalance());
}
console.log('component ran')
}, [dispatch]);
return (
<>
{balance.value ?? 0}
</>
);
}
In updateBalance action, you should set the balance status to 'fetching' and call the api. After api call is finished, you can set the balance status to 'fetched' and set the balance value to the fetched value. That's all I guess.

React: A service returning a ui component (like toast)?

Requirement: Show toast on bottom-right corner of the screen on success/error/warning/info.
I can create a toast component and place it on any component where I want to show toasts, but this requires me to put Toast component on every component where I intend to show toasts. Alternatively I can place it on the root component and somehow manage show/hide (maintain state).
What I am wondering is having something similar to following
export class NotificationService {
public notify = ({message, notificationType, timeout=5, autoClose=true, icon=''}: Notification) => {
let show: boolean = true;
let onClose = () => {//do something};
if(autoClose) {
//set timeout
}
return show ? <Toast {...{message, notificationType, onClose, icon}} /> : </>;
}
}
And call this service where ever I need to show toasts.
Would this be the correct way to achieve the required functionality?
You can use AppContext to manage the state of your toast and a hook to trigger it whenever you want.
ToastContext:
import React, { createContext, useContext, useState } from 'react';
export const ToastContext = createContext();
export const useToastState = () => {
return useContext(ToastContext);
};
export default ({ children }) => {
const [toastState, setToastState] = useState(false);
const toastContext = { toastState, setToastState };
return <ToastContext.Provider value={toastContext}>{children}</ToastContext.Provider>;
};
App:
<ToastProvider>
<App/>
<Toast show={toastState}/>
</ToastProvider>
Then anywhere within your app you can do:
import {useToastState} from 'toastContext'
const {toastState, setToastState} = useToastState();
setToastState(!toastState);

How to fetch Data on load component using React-Redux and Axios?

I have a need to fetch data from an API on component load, am using axios to fetch data, I need to save the response to the state and get back when the component load.
But i could do as am new to this.
My codes as below.
Sales.js : (This is where I fetch My components)
function SalesDesk() {
return (
<div>
<FoodScreen />
</div>
)}
export default SalesDesk;
FoodScreen.js (This is where i need to list my results to a variable, to map it later)
function FoodScreen() {
return(
<div className="sdFoodScreenMain">
{console.log(items)} // The results should be displayed here
</div>
)}
export default FoodScreen;
API.js (Here is where where i use my axios Router)
const API_URL = `https://jsonplaceholder.typicode.com/`; //Mock api for test purposes
export const GetAllItems = () => {
return (dispatch) => {
axios.get(API_URL)
.then(response => {
dispatch(allItemsList(response.data));
})
}
};
ItemsReducer.js (The reducer Logic)
const ItemsReducer =(state:Array = null, action) =>{
if (action.type === 'ALL_ITEMS') {
return GetAllItems ;
} else {
return state= null;
}
};
export default ItemsReducer
SalesAction.js (Action list)
export const allItemsList = () => {
return {
type: 'ALL_ITEMS'
};
};
All I need to do is fetch the the data from the API and display it in the console, when the component renders.so that I can display it in a map of div boxes for future purposes. Am new to both react and Redux, so ignore if any logic or implementation issues.
At first Router.js is a bad name(api.js etc), You should connect Sales.js to redux, using { connect } from 'react-redux'. See there https://redux.js.org/basics/usage-with-react and call action to fetch data in Sales.js
All I had to add an useDispatch on the component render, so it could fetch the data to the component on load.
import Reactfrom 'react'
import {useDispatch} from "react-redux";
import {GetAllItems} from 'API' //File containing the axios function
export function SalesDesk() {
const dispatch = useDispatch();
dispatch(GetAllItems())
return (
<div>
<FoodScreen />
</div>
)}
This helped me to fetch add add to state on component load.

Resources