I am new to redux hooks and react hooks and this is my code that doesn't stop rendering?
const App: React.FC = () => {
const dispatch = useDispatch();
const [page, setPage] = useState(1);
const fetchUsers = async (page: number) => {
dispatch({
type: FETCH_USERS_REQUEST,
payload: { page }
});
try {
const { data, ...result } = await api.fetchUsers(page);
const user = new schema.Entity('data');
const users = normalize(
{ users: data },
{
users: [user]
}
);
dispatch({
type: FETCH_USERS_SUCCESS,
payload: {
...result,
users
}
});
} catch (error) {
dispatch({ type: FETCH_USERS_FAILURE, payload: { error } });
}
};
useEffect(() => {
fetchUsers(1);
}, [fetchUsers]);
const users = useSelector((state: RootState) => state.users);
console.log('asd', users);
return (
<div className="vh-100 vw-100">
<header>Users</header>asdasd
</div>
);
};
fetchUsers is an async method that i plan to use multiple times on loadMore and pagination, however, this is not working, how do i make it work?
Your fetchUsers is changing on each rerender that is casing your useEffect with that fetch to trigger.
Try this:
useEffect(() => {
fetchUsers(pageNumber);
}, [pageNumber]);
Related
So I am building an e-commerce website checkout page with commerce.js. I have a context that allows me to use the cart globally. But on the checkout page when I generate the token inside useEffect , the cart variables have not been set until then.
My context is as below
import { createContext, useEffect, useContext, useReducer } from 'react';
import { commerce } from '../../lib/commerce';
//Provides a context for Cart to be used in every page
const CartStateContext = createContext();
const CartDispatchContext = createContext();
const SET_CART = 'SET_CART';
const initialState = {
id: '',
total_items: 0,
total_unique_items: 0,
subtotal: [],
line_items: [{}],
};
const reducer = (state, action) => {
switch (action.type) {
case SET_CART:
return { ...state, ...action.payload };
default:
throw new Error(`Unknown action: ${action.type}`);
}
};
export const CartProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const setCart = (payload) => dispatch({ type: SET_CART, payload });
useEffect(() => {
getCart();
}, []);
const getCart = async () => {
try {
const cart = await commerce.cart.retrieve();
setCart(cart);
} catch (error) {
console.log('error');
}
};
return (
<CartDispatchContext.Provider value={{ setCart }}>
<CartStateContext.Provider value={state}>
{children}
</CartStateContext.Provider>
</CartDispatchContext.Provider>
);
};
export const useCartState = () => useContext(CartStateContext);
export const useCartDispatch = () => useContext(CartDispatchContext);
Now on my checkout page
const CheckoutPage = () => {
const [open, setOpen] = useState(false);
const [selectedDeliveryMethod, setSelectedDeliveryMethod] = useState(
deliveryMethods[0]
);
const [checkoutToken, setCheckoutToken] = useState(null);
const { line_items, id } = useCartState();
useEffect(() => {
const generateToken = async () => {
try {
const token = await commerce.checkout.generateToken(id, {
type: 'cart',
});
setCheckoutToken(token);
} catch (error) {}
};
console.log(checkoutToken);
console.log(id);
generateToken();
}, []);
return <div> {id} </div>; //keeping it simple just to explain the issue
};
In the above code id is being rendered on the page, but the token is not generated since on page load the id is still blank. console.log(id) gives me blank but {id} gives the actual value of id
Because CheckoutPage is a child of CartProvider, it will be mounted before CartProvider and the useEffect will be called in CheckoutPage first, so the getCart method in CartProvider hasn't been yet called when you try to read the id inside the useEffect of CheckoutPage.
I'd suggest to try to call generateToken each time id changes and check if it's initialised first.
useEffect(() => {
if (!id) return;
const generateToken = async () => {
try{
const token = await commerce.checkout.generateToken(id, {type: 'cart'})
setCheckoutToken(token)
} catch(error){
}
}
console.log(checkoutToken)
console.log(id)
generateToken()
}, [id]);
I need help. I don't understand why my dispatch action doesn't work. I've redux store currency list and current currency.
My reducer:
export const currencyReducer = (
state: typeState = initialState,
action: TypeActionCurrency
): typeState => {
switch (action.type) {
case types.CURRENCY_FILL_LIST:
return { ...state, list: action.payload }
case types.CURRENCY_SET_CURRENT:
return {
...state,
current:
state.list.find(currency => currency._id === action.payload) ||
({} as ICurrency),
}
default:
return state
}
}
My actions:
export const setCurrencyList = (currencies: ICurrency[]) => ({
type: types.CURRENCY_FILL_LIST,
payload: currencies,
})
export const setCurrentCurrency = (_id: string) => ({
type: types.CURRENCY_SET_CURRENT,
payload: _id,
})
My useEffect:
useEffect(() => {
if (!list.length) {
const fetchCurrencies = async () => {
try {
const data = await $apiClient<ICurrency[]>({ url: '/currencies' })
dispatch(setCurrencyList(data))
if (!current._id) dispatch(setCurrentCurrency(data[0]._id))
} catch (error) {
console.log(error)
}
}
fetchCurrencies()
}
}, [])
I want make request when load page and write currency list to Redux store, if we don't have current currency we write default currency from data.
There is one more strange thing, my redux extension shows that the state has changed, but when I receive it via the log or useSelector, it is empty
enter image description here
Thanks!
I am not 100% sure but it should work.
const [loader, setLoader] = useState(false);
const list = useSelector(state => state.list)
useEffect(() => {
if (!list.length) {
const fetchCurrencies = async () => {
try {
setLoader(true)
const data = await $apiClient<ICurrency[]>({ url: '/currencies' })
dispatch(setCurrencyList(data))
if (!current._id) dispatch(setCurrentCurrency(data[0]._id))
} catch (error) {
console.log(error)
} finally {
setLoader(false)
}
}
fetchCurrencies()
}
}, [])
useEffect(() => {
console.log(list);
}, [loader])
I'm trying to implement CRUD and retreive all data and put it into a table, so the console log return undefined, so I called the payload and it's not an array and I think that's the issue maybe. I'm using redux Thunk hooks. please can someone help me to solve it
here is the code bellow :
Component.jsx
import { retrieveCars } from '../action/cars.action'
const cars = useSelector(state => state.cars);
const dispatch = useDispatch();
useEffect(() => {
dispatch(retrieveCars());
});
cars.action.js
export const retrieveCars = () => async (dispatch) => {
try {
const res = await DataService.getAll();
dispatch({
type: RETRIEVE_CARS,
payload: res.data,
});
} catch (err) {
console.log(err);
}
};
Car Reducer:
const initialState = [];
function carReducer(cars = initialState, action) {
const { type, payload } = action;
switch (type) {
case CREATE_CAR:
return [...cars, payload];
case RETRIEVE_CARS:
return payload;
}
Car actions :
export const retrieveCars = () => async (dispatch) => {
try {
const res = await DataService.getAll();
dispatch({
type: RETRIEVE_CARS,
payload: res.data,
});
} catch (err) {
console.log(err);
}
};
The payload that I get isn't an array
It seems the payload you get is an array of objects if the screenshot above represents the payload.
Maybe you could try to refactor your code.
useEffect(async() => {
const res = await DataService.getAll();
dispatch(retrieveCars(res.data));
}, []);
Your action retriveCars could looks like this:
const retrieveCars = (payload) => {
({
type: RETRIEVE_CARS,
payload
});
};
I am not sure do you use Redux Thunk middleware in the code.
I am trying to load the user data and display on my dashboard page, I have tried many methods but failed to do so. Anyone can help?
below is my dashboard page:
const Dashboard = (props) => {
const { user } = props.auth;
useEffect(() => {
getCurrentProfile();
}, []);
return (<Fragment>
<h1>Dashboard</h1>
<i className='fas fa-user'></i>Welcome
<p>{user && user.fullName}</p>
</Fragment>);
};
below is my redux action:
export const getCurrentProfile = () => async dispatch => {
const [user] = useState('')
await authAxios.get('/user/profile').then(res => {
const result = res.data.user.result;
dispatch({
type: GET_PROFILE,
payload: res.data
});
}).catch(error => {
dispatch({
type: PROFILE_ERROR,
payload: {
msg: error.response.statusText,
status: error.response.status
}
});
});
};
I am rewriting a CRUD table with React hooks. The custom hook useDataApi below is for fetching data of the table, watching the url change - so it'll be triggered when params change. But I also need to fetch the freshest data after delete and edit. How can I do that?
const useDataApi = (initialUrl, initialData) => {
const [url, setUrl] = useState(initialUrl)
const [state, dispatch] = useReducer(dataFetchReducer, { data: initialData, loading: true })
useEffect(() => {
const fetchData = async () => {
dispatch({ type: 'FETCH_INIT' })
const result = await instance.get(url)
dispatch({ type: 'FETCH_SUCCESS', payload: result.data })
}
fetchData()
}, [url])
const doFetch = url => {
setUrl(url)
}
return { ...state, doFetch }
}
Since the url stays the same after delete/edit, it won't be triggered. I guess I can have an incremental flag, and let the useEffect monitor it as well. But it might not be the best practice? Is there a better way?
All you need to do is to take the fetchData method out of useEffect and call it when you need it. Also make sure you pass the function as param in dependency array.
const useDataApi = (initialUrl, initialData) => {
const [url, setUrl] = useState(initialUrl)
const [state, dispatch] = useReducer(dataFetchReducer, { data: initialData, loading: true })
const fetchData = useCallback(async () => {
dispatch({ type: 'FETCH_INIT' })
const result = await instance.get(url)
dispatch({ type: 'FETCH_SUCCESS', payload: result.data })
}, [url]);
useEffect(() => {
fetchData()
}, [url, fetchData]); // Pass fetchData as param to dependency array
const doFetch = url => {
setUrl(url)
}
return { ...state, doFetch, fetchData }
}