useSelector is defined after dispatching but then undefined - reactjs

I'm trying to get initial data from a reducer by dispatching action from App.js component, it works fine but when I switch to another component and load it with useSelector it gets undefined.
I have tried this line of code in Headphones.js but the second one returns undefined
const allData = useSelector((state) => state.allDataReducer);
const { loading, error, data } = allData;
App.js
const dispatch = useDispatch();
useEffect(() => {
dispatch(welcomeAction());
dispatch(listAllData);
}, [dispatch]);
allDataReducer.js
import {
LIST_ALL_DATA_FAIL,
LIST_ALL_DATA_REQUEST,
LIST_ALL_DATA_SUCCESS,
} from "../constants/shared";
export const allDataReducer = (state = { loading: true, data: {} }, action) => {
switch (action.type) {
case LIST_ALL_DATA_REQUEST:
return { loading: true };
case LIST_ALL_DATA_SUCCESS:
return { loading: false, data: action.payload };
case LIST_ALL_DATA_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
shared.js
import {
LIST_ALL_DATA_FAIL,
LIST_ALL_DATA_REQUEST,
LIST_ALL_DATA_SUCCESS,
} from "../constants/shared";
import Axios from "axios";
export const listAllData = async (dispatch) => {
dispatch({
type: LIST_ALL_DATA_REQUEST,
});
try {
const { data } = await Axios.get("/all");
dispatch({ type: LIST_ALL_DATA_SUCCESS, payload: data });
} catch (error) {
dispatch({ type: LIST_ALL_DATA_FAIL, payload: error.message });
}
};
Headphones.js
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { listheadphones } from "../actions/headphonesActions";
import BasicSection from "../components/BasicSection";
import Definer from "../components/Definer";
import LoadingBox from "../components/LoadingBox";
import MessageBox from "../components/MessageBox";
import ProductsCategories from "../components/ProductsCategories";
import BestAudioGear from "../components/BestAudioGear";
const Headphones = (props) => {
const dispatch = useDispatch();
const headphonesList = useSelector((state) => state.headphonesList);
const allData = useSelector((state) => state.allData);
const { loading, error, data } = allData; //undefined
//const { loading, error, headphones } = headphonesList;
console.log(headphonesList);
useEffect(() => {
dispatch(listheadphones());
}, [dispatch]);
return (
<div>
<Definer title="HEADPHONES" />
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
headphones.map((headphone) => (
<BasicSection
key={headphone.id}
name={headphone.headerName}
info={headphone.info}
mobile={headphone.mobile}
tablet={headphone.tablet}
desktop={headphone.desktop}
/>
))
)}
<ProductsCategories />
<BestAudioGear />
</div>
);
};
export default Headphones;
Github repo

Your description is still not specific enough, can't really pin down what the issue is. But here is some stuff I noticed:
dispatch(listAllData); somehow looks wrong to me, the action creator is usually a function that gets called: dispatch(listAllData());
Then where you define export const listAllData = async (dispatch) => { - this should be a function that returns a function if you're using the thunk middleware. You only defined a function.

Related

Why doesn't useEffect hook work on page refresh?

I'm working on a react project. I have my own API to fetch information. I'm using the useEffect hook to fetch profile information from API. My problem is when page mounts for the first time i can fetch the data with no problem but if i refresh the page it doesn't work at all. I know i have to give a second parameter to useEffect. I tried to put profile as the second argument even dispatched the getCurrentProfile function but when i do that it constantly fires off fetch request. I would be glad if anyone can help me with that. Thanks.
Here is my Profile component:
export const Profile = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getCurrentProfile());
}, [])
const profileReducer = useSelector((state) => state.profile);
const authReducer = useSelector((state) => state.auth);
const { profile, error, loading } = profileReducer;
const { user } = authReducer;
console.log("loading", loading)
console.log("profile", profile)
return loading && profile === null ? (
<div >
<Spinner />
</div>
) :
Here is my Profile action:
export const getCurrentProfile = () => async dispatch => {
try {
const res = await axios.get("/api/profile/me");
console.log(res);
dispatch({
type: "GET_PROFILE",
payload: res.data.data
})
} catch (err) {
dispatch({
type: "PROFILE_ERROR",
payload: { msg: err.response.statusText, status: err.response.status }
})
}
}
Here is my profile reducer:
export default (state = initialState, action) => {
const { type, payload } = action;
switch (type) {
case "GET_PROFILE":
return {
...state,
profile: payload,
loading: false
}
case "PROFILE_ERROR":
return {
...state,
error: payload,
profile: null
}
case "CLEAR_PROFILE":
return {
...state,
profile: null,
loading: false
}
default:
return state;
}
}
You might want to try adding conditional logic within the useEffect so you only trigger the dispatch if you don't already have a profile.
import "./styles.css";
import { useDispatch, useSelector } from "react-redux";
import { useEffect, useCallback } from "react";
import { getCurrentProfile } from "./action";
export const Profile = () => {
const dispatch = useDispatch();
const profileReducer = useSelector((state) => state.profile);
const authReducer = useSelector((state) => state.auth);
const { profile, error, loading } = profileReducer;
// read more about this here: https://stackoverflow.com/questions/58624200/react-hook-useeffect-has-a-missing-dependency-dispatch
const stableDispatch = useCallback(dispatch, []);
useEffect(() => {
if (!profile) {
stableDispatch(getCurrentProfile());
}
}, [profile, stableDispatch]);
const { user } = authReducer;
console.log("loading", loading);
console.log("profile", profile);
return loading && profile === null ? <div>Spinner</div> : "Actual Profile";
};
export default Profile;
Also, it doesn't seem like you're currently doing anything with the loading piece of state–at least from what you've shared here. You might want to dispatch an action indicating that you're loading before you start the fetch and then it will be set to false when you get the response.
Check out this codesandbox for reference: https://codesandbox.io/s/focused-kilby-gd2nr?file=/src/App.js
Reducers:
const initialState = {
profile: null,
loading: false
};
export const profile = (state = initialState, action) => {
const { type, payload } = action;
switch (type) {
case "LOADING_PROFILE":
return {
...state,
loading: true
};
case "GET_PROFILE":
return {
...state,
profile: payload,
loading: false
};
case "PROFILE_ERROR":
return {
...state,
error: payload,
profile: null
};
case "CLEAR_PROFILE":
return {
...state,
profile: null,
loading: false
};
default:
return state;
}
};
export const auth = (state = {}, action) => {
return state;
};
Action Creator:
import axios from "axios";
export const getCurrentProfile = () => async (dispatch) => {
try {
dispatch({ type: "LOADING_PROFILE" });
const res = await axios.get("https://jsonplaceholder.typicode.com/users/1");
console.log(res);
dispatch({
type: "GET_PROFILE",
payload: res.data.data
});
} catch (err) {
dispatch({
type: "PROFILE_ERROR",
payload: { msg: err.response.statusText, status: err.response.status }
});
}
};
index.js
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, combineReducers, applyMiddleware } from "redux";
import { profile, auth } from "./reducers";
import App from "./App";
import thunk from "redux-thunk";
const store = createStore(
combineReducers({
profile,
auth
}),
applyMiddleware(thunk)
);
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<Provider store={store}>
<App />
</Provider>
</StrictMode>,
rootElement
);
Well i solved it by dispatching 'getCurrentProfile' not 'getCurrentProfile()' turns out using it like a function causes continuously firing off.
const profileReducer = useSelector((state) => state.profile);
const authReducer = useSelector((state) => state.auth);
const { profile, error, loading } = profileReducer;
const dispatch = useDispatch();
useEffect(() => {
if (!profile) {
console.log("It worked")
dispatch(getCurrentProfile());
}
}, [dispatch(getCurrentProfile)])

Cannot get object values in react js

I am passing value into the redux store through reducer. And I am displaying that value in the component.
But it says cannot read property name of undefined
Even whats weird is when I map the product, I can see product value in the console and when I don't map, I don't see the product value in the console. Please help me with this
Please find the code here
Component
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { listProductDetails } from "./actions/productActions";
const Playground = () => {
const dispatch = useDispatch();
const productDetails = useSelector((state) => state.productDetails);
const { product } = productDetails;
useEffect(() => {
dispatch(listProductDetails("pod2"));
}, [dispatch]);
return (
<div>
<h1>
<h1>{product[0].name}</h1>
</h1>
</div>
);
};
export default Playground;
Reducer
export const productDetailsReducers = (state = { product: [] }, action) => {
switch (action.type) {
case PRODUCT_DETAILS_REQUEST:
return { loading: true, ...state };
case PRODUCT_DETAILS_SUCCESS:
return {
loading: false,
product: action.payload,
};
case PRODUCT_DETAILS_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
Action
export const listProductDetails = (id) => async (dispatch) => {
try {
dispatch({ type: PRODUCT_DETAILS_REQUEST });
const { data } = await axios.get(`/api/products/${id}`);
console.log("this is the data");
console.log(data);
dispatch({ type: PRODUCT_DETAILS_SUCCESS, payload: data });
} catch (error) {
dispatch({
type: PRODUCT_DETAILS_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
Store
const reducer = combineReducers({
productDetails: productDetailsReducers,
});
It's always better to have a condition before accessing the data
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { listProductDetails } from "./actions/productActions";
const Playground = () => {
const dispatch = useDispatch();
const productDetails = useSelector((state) => state.productDetails);
const { product } = productDetails;
useEffect(() => {
dispatch(listProductDetails("pod2"));
}, [dispatch]);
return (
<div>
{
product &&
product.length ?
<h1>
<h1>{product[0].name || ""}</h1>
</h1>
: null
}
</div>
);
};
export default Playground;

Async call results to Warning: Maximum update depth exceeded in React & Redux

I have an app built with React, Redux that pulls data from a RESTful service sitting in my local. I tested the implementation with dummy data and works fine. However, when I hook up the async service the calls result in havoc with the below error:
Here is the code
reducer.js
import {
LOAD_ALL_PRODUCTS_SUCCESS,
LOAD_ALL_PRODUCTS_REQUEST,
LOAD_ALL_PRODUCTS_FAIL,
LOAD_PRODUCT_REQUEST,
LOAD_PRODUCT_SUCCESS,
LOAD_PRODUCT_FAIL,
} from './actions';
export const productData = (state = { loader: {}, products: [] }, action) => {
const { type, payload } = action;
switch (type) {
case LOAD_ALL_PRODUCTS_REQUEST: {
return { loader: true, products: [] };
}
case LOAD_ALL_PRODUCTS_SUCCESS: {
return { loader: false, products: payload };
}
case LOAD_ALL_PRODUCTS_FAIL: {
return { loader: false, error: payload };
}
default:
return state;
}
};
thunk.js
import axios from 'axios';
import { mockData } from '../MockData';
import {
loadAllProductFailed,
loadAllProductRequest,
loadAllProductSuccess,
LOAD_PRODUCT_FAIL,
LOAD_PRODUCT_REQUEST,
LOAD_PRODUCT_SUCCESS,
} from './actions';
export const loadInitialProducts = () => async (dispatch) => {
try {
dispatch(loadAllProductRequest());
//this is where the issues is
const response = await axios.get('http://localhost:8080/v1/product/all');
const payload = await response.data;
console.log(payload);
dispatch(loadAllProductSuccess(payload));
} catch (error) {
dispatch(
loadAllProductFailed(
error.response && error.response.data.message
? error.response.data.message
: error.message
)
);
}
};
export const loadProductDetails = (id) => async (dispatch) => {
try {
dispatch({ type: LOAD_PRODUCT_REQUEST });
//do a axios api call for product api
dispatch({
type: LOAD_PRODUCT_SUCCESS,
payload: mockData.find(({ productId }) => productId == id),
});
} catch (error) {
dispatch({
type: LOAD_PRODUCT_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
export const LOAD_ALL_PRODUCTS_REQUEST = 'LOAD_PRODUCTS_REQUEST';
export const loadAllProductRequest = () => ({
type: LOAD_ALL_PRODUCTS_REQUEST,
});
export const LOAD_ALL_PRODUCTS_SUCCESS = 'LOAD_ALL_PRODUCTS_SUCCESS';
export const loadAllProductSuccess = (payload) => ({
type: LOAD_ALL_PRODUCTS_SUCCESS,
payload: payload,
});
export const LOAD_ALL_PRODUCTS_FAIL = 'LOAD_ALL_PRODUCTS_FAIL';
export const loadAllProductFailed = (error) => ({
type: LOAD_ALL_PRODUCTS_FAIL,
payload: error,
});
export const LOAD_PRODUCT_REQUEST = 'LOAD_PRODUCT_REQUEST';
export const loadProductRequest = () => ({
type: LOAD_ALL_PRODUCTS_FAIL,
});
export const LOAD_PRODUCT_SUCCESS = 'LOAD_PRODUCT_SUCCESS';
export const loadProductSuccess = (payload) => ({
type: LOAD_ALL_PRODUCTS_FAIL,
payload: payload,
});
export const LOAD_PRODUCT_FAIL = 'LOAD_PRODUCT_FAIL';
export const loadProductFailed = (error) => ({
type: LOAD_ALL_PRODUCTS_FAIL,
payload: error,
});
Home.js
import React, { useState, useEffect } from 'react';
import { conwayAPI } from '../ConwayAPI';
import { Container, Col, Row } from 'react-bootstrap';
import Product from './Product';
import { useDispatch, useSelector } from 'react-redux';
import _ from 'lodash';
import { loadInitialProducts } from '../app/thunk';
const Home = () => {
//local state maintained only for this component
const [filterProducts, setFilterProducts] = useState([]);
const dispatch = useDispatch();
const productList = useSelector((state) => state.productData);
const { loader, error, products } = productList;
useEffect(() => {
dispatch(loadInitialProducts());
}, [dispatch, products]);
const doSearch = (text) => {
_.isEmpty(text)
? setFilterProducts(products)
: setFilterProducts(
filterProducts.filter((product) =>
product.productName.toLowerCase().includes(text.toLowerCase())
)
);
};
return (
<Container fluid>
<Row md={7} lg={5} className='justify-content-md-center'>
{filterProducts.length &&
filterProducts.map((datam, key) => {
return (
<Col key={key}>
<Product product={datam} key={key} />
</Col>
);
})}
</Row>
</Container>
);
};
export default Home;
When I click on a Nav panel the Home.js gets called and the error starts. What can I do differently to eliminate this error?

How can I avoid the infinite loop in useEffect?

I need your help. I'm creating an app with useContext and useReducer hooks and I a have problems. I have a function to get all notes from my database. I called that function inside off useEffect hook:
import React, { useContext, useEffect } from "react";
import { useTranslation } from "react-i18next";
//Context
import AuthContext from "../../context/auth/authContext";
import NoteContext from '../../context/notes/noteContext';
//Components
import { Row, Col, Container, Button } from "react-bootstrap";
import Canva from '../Canva/Canva';
import Note from '../Note/Note';
const Dashboard = () => {
const { t, i18n } = useTranslation();
const authContext = useContext(AuthContext);
const { authUser, user } = authContext;
const noteContext = useContext(NoteContext);
const { notes, getNotes, addNote } = noteContext;
useEffect(() => {
getNotes();
}, []);
return (
<>
<Container>
<Row>
<Col sm={12} md={10}>
<Button onClick={() => addNote()} type='button' className='mb-2'>
Añadir elemento
</Button>
<Canva>
{notes && (notes.map(note => {
return (
<Note key={note._id} note={note} />
)
}))}
</Canva>
</Col>
</Row>
</Container>
</>
);
};
export default Dashboard;
If I called that function that way, my state doesn't change:
notes: undefined
But if I introduce a dependency inside of useEffect, my app goes into an infinite loop. For example:
useEffect(() => {
getNotes();
}, [notes])
//Or:
useEffect(() => {
getNotes()
}, [getNotes])
How can I avoid the infinite loop?
You need to use 2 useEffect hooks, one for fetch data and second to proceed it:
useEffect(() => {
getNotes();
}, []);
useEffect(() => {
if (notes && notes.length) {
....setState or what else
}
}, [notes]);
My note state looks like:
import React, { useReducer } from 'react';
import clientAxios from '../../config/clientAxios';
import NoteContext from './noteContext';
import NoteReducer from './noteReducer';
import {
GET_NOTES,
ADD_NOTE,
DELETE_NOTE,
UPDATE_NOTE,
} from '../../types';
const NoteState = ({ children }) => {
const initialState = {
notes: [],
noteError: false,
};
const [state, dispatch] = useReducer(NoteReducer, initialState);
const getNotes = async () => {
try {
const response = await clientAxios.get('/user/Notes');
dispatch({
type: GET_NOTES,
payload: response.data
})
} catch (error) {
console.log(error.response);
}
}
const addNote = async data => {
try {
const response = await clientAxios.post('/addNote', data);
dispatch({
type: ADD_NOTE,
payload: response.data.data
})
} catch (error) {
console.log(error.response);
}
}
const updateNote = async (id, { title, description }) => {
try {
const response = await clientAxios.put(`updateNote/${id}`, { title, description });
console.log(response.data);
dispatch({
type: UPDATE_NOTE,
payload: response.data
})
} catch (error) {
console.log(error.response)
}
}
const deleteNote = async id => {
try {
await clientAxios.put(`/deleteNote/${id}`);
dispatch({
type: DELETE_NOTE,
payload: id
})
} catch (error) {
console.log(error.response);
}
}
return(
<NoteContext.Provider
value={{
notes: state.notes,
noteError: state.noteError,
getNotes,
addNote,
updateNote,
deleteNote,
}}
>
{children}
</NoteContext.Provider>
);
}
export default NoteState;
and my reducer:
import {
GET_NOTES,
ADD_NOTE,
DELETE_NOTE,
UPDATE_NOTE,
} from '../../types';
export default (action, state) => {
switch(action.type) {
case GET_NOTES:
return {
...state,
notes: action.payload
}
case ADD_NOTE:
return {
...state,
notes: [...state.notes, action.payload]
}
case UPDATE_NOTE:
return {
...state,
notes: state.notes.map(note => note._id === action.payload._id ? action.payload : note)
}
case DELETE_NOTE:
return {
...state,
notes: state.notes.filter(note => note._id !== action.payload),
}
default:
return state;
}
}

Using useEffect with React-redux

I have a problem. As I understood hook useEffect doen't run.
I have action that should take data from server.
export const getProducts = () => {
return dispatch => {
dispatch(getProductsStarted());
fetch('https://shopserver.firebaseapp.com/get-products')
.then(res => {
dispatch(getProductsSuccess(res.json()));
})
.catch(err => {
dispatch(getProductsFailure(err.message));
});
}
}
const getProductsSuccess = todo => ({
type: "ADD_TODO_SUCCESS",
payload: {
...todo
}
});
const getProductsStarted = () => ({
type: "ADD_TODO_STARTED"
});
const getProductsFailure = error => ({
type: "ADD_TODO_FAILURE",
payload: {
error
}
});
I have a reducer.
const initialState = {
loading: false,
products: [],
error: null
}
export const ProductReducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_TODO_SUCCESS":
return {
...state,
loading: false,
error: null,
todos: [...state.products, action.payload.products]
}
case "ADD_TODO_STARTED":
return {
...state,
loading: true
}
case "ADD_TODO_FAILURE":
return {
...state,
loading: false,
error: action.payload.error
}
default:
return state
}
}
And I have a Component where I want to render a result.
import React from 'react';
import { CardItem } from "./cardItem";
import { useSelector } from 'react-redux';
import { useEffect } from 'react';
import { getProducts } from '../Redux/Actions/productAction'
export const ProductCard = () => {
useEffect(() => {
getProducts();
console.log('111111')
})
const data = useSelector(state => state.ProductReducer.products);
return (
<div>
{data.map( element =>
CardItem (element)
)}
</div>
)
}
After rendering page nothing happens. ReduxDevTools shows that there was no send actions. Please, help me to fix it. Thank you.
I think you should be calling your async action like this :
import { useDispatch, useSelector } from 'react-redux';
[...]
export const ProductCard = () => {
const dispatch = useDispatch();
useEffect(() => {
// I guess getProducts is an async redux action using redux-thunk
dispatch(getProducts());
console.log('111111')
}, []);
[...]
}
I assume you want to load products only when component is born, so I pass an empty array as second argument for useEffect (https://reactjs.org/docs/hooks-reference.html#useeffect).

Resources