This the code that is responsible for getting Data and updating the list:
import {createSlice} from "#reduxjs/toolkit";
import axios from "axios";
const initialState = {
log: []
}
let page = 1;
const cartSlice = createSlice({
name: 'cart',
initialState: initialState,
reducers: {
fetching: (state, payload) => {
state.log = axios.get(`http://localhost:5000/fetch/?${page = payload.payload}`)
// console.log(state)
}
}
})
export const {fetching} = cartSlice.actions;
export default cartSlice.reducer;
And this is the Home Page:
import React, {useEffect} from "react";
import {useDispatch, useSelector} from "react-redux";
import {fetching} from "../features/cardSlice";
import Cards from "./Cards";
export default function HomePage(){
const dispatch = useDispatch();
const {itemsList} = useSelector((store) => store.card)
console.log(itemsList)
const {pageNumber} = useSelector((store) => store.page);
useEffect(()=>{
dispatch(fetching(1)); **// This is where i call dispatch to update the state and get the data**
})
function cardMapper(items) {
return(
<Cards
name = {items.name}
key = {items.id}
cuisine={items.cuisine}
address={items.address}
/>
)
}
return(
<div>
{/*{itemsList.map(cardMapper)}*/}
</div>
)
}
When i run this on localhost i am not able to get data, the console.log(itemList) is showing undefined and also the dispatch(fetching(1)) is called infinite times.
Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
I am not able to understand why i'm getting an infinite loop and also why am i not getting the data.
I modified your code. To prevent infinite loop in using useEffect you should put [] as dependency
And for the useSelector,the variable should not enclosed by {}.
see the modified code below.
As I see, it should be cart not card
Try this code instead
export default function HomePage(){
const dispatch = useDispatch();
const itemsList = useSelector((store) => store.cart)
console.log(itemsList)
const {pageNumber} = useSelector((store) => store.page);
useEffect(()=>{
dispatch(fetching(1)); **// This is where i call dispatch to update the state and get the data**
},[])
function cardMapper(items) {
return(
<Cards
name = {items.name}
key = {items.id}
cuisine={items.cuisine}
address={items.address}
/>
)
}
return(
<div>
{/*{itemsList.map(cardMapper)}*/}
</div>
)
}
also in reducer
add a return
fetching: async (state, payload) => {
const newState = await axios.get(`http://localhost:5000/fetch/?${page = payload.payload}`)
return newState;
}
// this should end the infinite loop
useEffect(()=>{
dispatch(fetching(1)); // This is where i call dispatch to update the state and get the data
}, [ ])
You need added dependencies as an empty array. To remove the yellow
squiggly line you need to add dispatch inside that dependencies array.
however, react to ensure that dispatch dependencies never change and
that you are not responsible for re-render your component.
useEffect(()=>{
dispatch(fetching(1))
},[dispatch])
Related
I seem to have an infinite loop in my code but I can't seem to see where, I usually see this if I am setting the state of something that is a dependency of useEffect but here the two variables (values / items) are completely seperate.
App.js
import React from 'react';
import './style.css';
import MyComponent from './MyComponent.js';
export default function App() {
return (
<div>
<MyComponent />
</div>
);
}
MyComponent.js
import React, { useEffect, useState } from 'react';
const MyComponent = ({ values = [] }) => {
console.log('MyComponent Reloaded');
const [items, setItems] = useState();
useEffect(() => {
const loadItems = () => {
//setItems([]); //why does this cause infinite render loop?
};
loadItems();
}, [values]);
return <></>;
};
export default MyComponent;
Why does this cause a render loop?
I have an example build Here (Uncomment the commented line to begin render loop)
You need to introduce the default values of the props the right way:
import React, { useEffect, useState } from 'react';
const MyComponent = ({ values }) => {
console.log('MyComponent Reloaded');
const [items, setItems] = useState();
useEffect(() => {
console.log(values)
const loadItems = () => {
setItems([]);
};
loadItems();
}, [values]);
return <></>;
};
MyComponent.defaultProps = {
values: []
}
export default MyComponent;
There are two possible bugs in the code:
According to your code, values variable is created every time, when checked with the previous values variable it is not same. So it causes infinite loop.
To fix this use default props.
const MyComponent = ({ values }) => {
...
MyComponent.defaultProps = {
values: []
}
As in your question,this line causes you the infinite loop
//setItems([]);
To overcome this
Add items dependency along with values, so that both values are watched before re-rendering.
useEffect(() => {
console.log(values)
const loadItems = () => {
setItems([]);
};
loadItems();
}, [values,items]);
I have this redux todo app that updates the state of the remaining tasks based on the number of incomplete tasks.
The app is working without any errors or problems but when I add a task, toggle completion, and remove a task, the action type of remainingTasks/updateRemainingTasks fires twice:
Interestingly, that action only fires once when removing a task that has been completed:
These are the code for that slice and its corresponding component:
SLICE
import { createSlice } from "#reduxjs/toolkit";
const remainingTasksSlice = createSlice({
name: "remainingTasks",
initialState: 0,
reducers: {
updateRemainingTasks: (state, action) => {
return action.payload;
},
},
});
// Selectors
export const selectRemainingTasksSlice = (state) => state.remainingTasksReducer;
// Actions
export const { updateRemainingTasks } = remainingTasksSlice.actions;
// Reducers
export default remainingTasksSlice.reducer;
COMPONENT
import { useSelector, useDispatch } from "react-redux";
import {
selectRemainingTasksSlice,
updateRemainingTasks,
} from "./remainingTasksSlice";
import { selectTaskSlice } from "../task/taskSlice";
const RemainingTasks = () => {
const dispatch = useDispatch();
const remainingTasksSlice = useSelector(selectRemainingTasksSlice);
const taskSlice = useSelector(selectTaskSlice);
// Number of Incomplete Tasks
const incompleteTasks = taskSlice.filter((task) => !task.completed).length;
// Update the State of the Remaining Tasks
dispatch(updateRemainingTasks(incompleteTasks));
return (
<div>
<h1 className="header">
{remainingTasksSlice > 1
? `${remainingTasksSlice} Tasks Left`
: `${remainingTasksSlice} Task Left`}
</h1>
</div>
);
};
export default RemainingTasks;
I was wondering if this is a normal thing or my code isn't well optimized.
I think you have to call dispatch into a useEffect hook:
....
useEffect(()=>{
// Number of Incomplete Tasks
const incompleteTasks = taskSlice.filter((task) => !task.completed).length;
// Update the State of the Remaining Tasks
dispatch(updateRemainingTasks(incompleteTasks));
}, [taskSlice]);
....
otherwise you call dispatch every time you render the Component.
I have looked into multiple sources trying to solve this problem but could not find any answers. I have a functional component <Dashboard /> which will display some information from an API.
I expected the component to first get into useEffect, execute the getData function and then display {devices} on the screen. What happens, though, is that the store state is updated, but the component not. The {devices} variable is always undefined. I don't think I understand how to access my state variable from reducers/all/dashboard.js with useSelector.
dashboard/index.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import api from "../../services/api";
import * as DashboardActions from "../../store/actions/dashboard";
const Dashboard = (props) => {
const dispatch = useDispatch();
const devices = useSelector(state => state.device)
useEffect(() => {
async function getData() {
const pathname = "/dashboard";
await api
.get(pathname)
.then((res) => {
dispatch(DashboardActions.setData(res.data));
})
.catch((res) => {
console.log(res.response.data);
});
}
getData();
console.log("devices ue ", devices);
}, [dispatch]);
return (
<div>
<h1>Dashboard</h1>
<span>{devices}</span>
</div>
);
};
export default Dashboard;
reducers/all/dashboard.js
const INITIAL_STATE = {
devices: [],
};
function dashboard(state = INITIAL_STATE, action) {
console.log("Action ", action)
if ("DASHBOARD_SET_DATA" === action.type) {
const data = action.data;
console.log("Data: ", data.devices)
state = { ...state, devices: data.devices };
console.log("State ", state)
}
return state;
}
export default dashboard;
actions/dashboard.js
export function setData(data) {
return {
type: "DASHBOARD_SET_DATA",
data,
};
}
I would appreciate any help a lot.
Thanks in advance!
The react-redux useSelector hook is selecting state from your redux store state object.
If your dashboard reducer is combined into your root reducer, something like
const rootReducer = combineReducers({
... other reducers
dashboard,
... other reducers
});
Then the devices state value should be accessed from state.dashboard.devices.
The update for your component:
const devices = useSelector(state => state.dashboard.devices)
In my code below, I have a delete button that should be deleting the data if clicked. However, when I click on it, I am seeing through console.log that it is returning undefined instead of the id number. Can't seem to figure out why. Any help will be greatly appreciated. Thank you.
//Actions File
export const GET_ITEMS = 'GET ITEMS';
export const FETCH_ITEMS_SUCCESS = 'FETCH ITEMS SUCCESS';
export const FETCH_ITEMS_ERROR = 'FETCH ITEMS ERROR';
export const DELETE_ITEM = 'DELETE_ITEM';
export const getItems = () => ({
type: GET_ITEMS
});
export const deleteItem = (itemId) => ({
type : DELETE_ITEM,
payload: itemId
});
//App.js
class App extends Component {
componentDidMount() {
this.props.getItems()
}
static propTypes = {
getItems: PropTypes.func.isRequired,
deleteItem: PropTypes.func.isRequired
}
handleDelete = (id) =>{
this.props.deleteItem(id)
console.log(this.props.deleteItem(id));
}
render() {
const { itemsList} = this.props.items
return (
<div className="container app-wrapper">
<header>
{itemsList.map(item => (<h1 key={item.id}>{item.title} <button onClick={this.handleDelete.bind(this, item.id)}>delete</button></h1>))}
</header>
</div>
);
}
}
const mapStateToProps = state => ({
items: state.items
});
export default connect(mapStateToProps, {getItems, deleteItem})(App);
The dispatched action should return undefined, because it does not return anything. You are misunderstanding how data flows in the Redux/reducer pattern.
Here's the basic flow of a Redux update:
Action is dispatched.
All reducers receive the action object.
All reducers return their new or previous state depending on that action's contents.
connect sees that the Redux state has changed, and triggers a re-render of the children components.
You may now use the updated data from your Redux store through props (mapped in mapStateToProps).
You cannot call an action and receive the updated state as the return value. It breaks the fundamental pattern of how data flows/updates in Redux.
You are referencing your delete action incorrectly in connect. deleteItem expects an id param passed into it.
Try this,
const mapStateToProps = state => ({
items: state.items
});
const mapDispatchToProps = (dispatch) =>
{
return {
deleteItem: (id) => dispatch(actions.deleteItem(id)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
Should I ignore 'React Hook useEffect has a missing dependency' warning?
Usually when I am getting data from an API this is what I do:
const Component = () => {
const [data,setData] = useState([]);
const getData = () => {
//Getting data and set data code...
}
useEffect(()=>{
getData();
},[]);
}
and recently I am trying out use redux to do the same thing(getting data from API) and I got this 'React Hook useEffect has a missing dependency' warning...
action:
import {GET_POSTS} from './types';
const getPosts = () => (dispatch) => {
const url = 'https://jsonplaceholder.typicode.com/posts';
fetch(url)
.then(res => res.json())
.then(data => {
dispatch({
type: GET_POSTS,
payload: data
});
});
}
export default getPosts;
reducer:
import {GET_POSTS} from '../actions/types';
const initialState = {
posts: []
}
const postsReducer = (state = initialState, action) => {
switch(action.type){
case GET_POSTS:
return {
...state,
posts: action.payload
}
default:
return state;
}
}
export default postsReducer;
app.js:
import React, {useEffect} from 'react';
import {connect} from 'react-redux';
import Hello from './components/Hello';
import getPost from './actions/postsAction';
import './App.css';
const App = ({getPost, dispatch}) => {
useEffect(() => {
getPost();
},[]);
return (
<div className='App'>
<Hello/>
</div>
);
};
const mapdispatchtoprops = (dispatch) => ({
dispatch,
getPost: () => {
dispatch(getPost());
}
});
export default connect(null, mapdispatchtoprops)(App);
Is there a way to fix this problem, I have tried to put dispatch inside the useEffect array but the warning still shows, like this:
useEffect(() => {
getPost();
},[dispatch]);
This is the full warning: React Hook useEffect has a missing dependency: 'getPost'. Either include it or remove the dependency array react-hooks/exhaustive-deps
Tried to remove the useEffect array but I'll get infinite loop, it'll just keeps getting the data from the api(I only need it to run once).
Should I ignore the warning? if not, whats the best practice way to handle this problem?
I never got this kind of warning before when I left the useEffect array empty but got it recently, why?
The error message is telling you what you to do. Just add getData to the dependencies array like so: [dispatch, getData]. Anything external you reference within your useEffect (like a function) should be part of the dependency list so it can trigger the effect whenever the value changes. In your case it likely won't, but React is warning you just to be safe. Hope that helps!
You may want to start thinking from a different perspective. You are apparently trying to do side effect of loading data after component got rendered. So just inject your data via redux or propagation props from parent and remove array altogether. I.e.
const Component = ({posts}) => {
const getData = () => {
//Getting data and set data code...
}
useEffect(() => {
if (!posts) {
getData();
}
});
....
}
Your posts will be loaded once and useEffect's function should only care about posts is there or not.