I'm trying to create a pie chart by making changes to my variable using Redux on React-Native, but even if the dispatch
operation is successful in my variable, when I use useSelector
, there is a one-time change. Even if my next dispatch
operation is successful, useSelector
does not refresh the data and I cannot see it on the screen.
Home screen:
import React from 'react';
import {ScrollView, View, Text, Button} from 'react-native';
import {useSelector} from 'react-redux';
import PieCard from '../components/PieCard/PieCard';
import DailyInfo from '../components/DailyCard/DailyInfoCard';
import CustomHeader from '../components/CustomHeader/CustomHeader';
const Home = ({navigation, route}) => {
//I'm pulling data with useSelector
const selectorData = useSelector(item => item.userData);
const selectorData2 = useSelector(item => item.dailyData);
const [userInfo, setUserInfo] = React.useState(selectorData);
const [dailyData, setDailyData] = React.useState(selectorData2);
const dayControl = dailyData.time.split('/');
const days = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
//I want to update when there is a change in useSelector
React.useEffect(() => {
setUserInfo(selectorData);
setDailyData(selectorData2);
}, [selectorData, selectorData2]);
//preparing data for pie
const setPieData = (remaining, consumed) => {
return {
dataRate: [
{
x:
consumed > remaining
? ' '
: `${Math.round(100 * (remaining - consumed)) / 100} g`,
y: consumed > remaining ? 0 : remaining - consumed,
},
{x: `${consumed} g`, y: consumed},
],
color: ['#ff0000', '#80ce48'],
};
};
//return my app
return (
<ScrollView style={{flex: 1, backgroundColor: '#eeeeee'}}>
<CustomHeader title={days[new Date().getDay()]} navigation={navigation} />
<View style={{flex: 1, marginHorizontal: 10}}>
{userInfo.daily_calori > 0 ? (
<DailyInfo
calories={Math.round(100 * (userInfo.daily_calori / 4)) / 100}
fat={Math.round(100 * (userInfo.daily_fat / 4)) / 100}
protein={Math.round(100 * (userInfo.daily_protein / 4)) / 100}
carbohydrate={
Math.round(100 * (userInfo.daily_carbohydrate / 4)) / 100
}
/>
) : (
<Text style={{flex: 1, alignItems: 'center'}}>
You must enter your information on the profile page.
</Text>
)}
{dailyData.protein > 0 && dayControl[0] === `${new Date().getDay()}` ? (
<View>
<PieCard
data={setPieData(userInfo.daily_calori / 4, dailyData.calories)}
header="Calories"
title1="remaining"
title2="consumed"
/>
<PieCard
data={setPieData(userInfo.daily_fat / 4, dailyData.fat)}
header=" Fat"
title1="remaining"
title2="consumed"
/>
<PieCard
data={setPieData(userInfo.daily_protein / 4, dailyData.protein)}
header="Protein"
title1="remaining"
title2="consumed"
/>
<PieCard
data={setPieData(
userInfo.daily_carbohydrate / 4,
dailyData.carbohydrate,
)}
header="Carbohydrate"
title1="remaining"
title2="consumed"
/>
</View>
) : null}
</View>
</ScrollView>
);
};
export default Home;
My reducer:
import AsyncStorage from '@react-native-async-storage/async-storage';
export default function (state, action) {
let userData = state.userData;
let dailyData = state.dailyData;
let weeklyMenu = state.weeklyMenu;
let montly = state.montlyData;
switch (action.type) {
case 'USER_INFO':
userData = action.payload.data;
try {
const jsonValue = JSON.stringify(userData);
AsyncStorage.setItem('user_info', jsonValue);
} catch (err) {
console.log('user set err', err);
}
return {...state, userData};
case 'ADD_FOOD':
let {food, year, month, date, day, meal} = action.payload;
let time = `${day}/${date}/${month}/${year}`;
const dayControl = dailyData.time.split('/');
if (time !== dailyData.time) {
dailyData = {protein: 0, fat: 0, carbohydrate: 0, calories: 0};
montly.days.push(time);
if (montly.days.length > 31) {
montly.days.shift();
montly.data.shift();
}
if (`${day}` === dayControl[0] && `(${date}` !== dayControl[1]) {
weeklyMenu.week[day] = [[], [], [], []];
weeklyMenu.foodId[day] = [[], [], [], []];
}
}
dailyData.time = time;
console.log('zaman', dailyData.time);
dailyData.protein = Math.round(100 * food.full_nutrients[0].value) / 100;
dailyData.fat = Math.round(100 * food.full_nutrients[1].value) / 100;
dailyData.carbohydrate =
Math.round(100 * food.full_nutrients[2].value) / 100;
dailyData.calories =
Math.round(100 * food.full_nutrients[3].value) / 100;
let monthData = [
dailyData.protein,
dailyData.fat,
dailyData.carbohydrate,
dailyData.calories,
];
weeklyMenu.week[day][meal].push(food.food_name);
weeklyMenu.foodId[day][meal].push(food.nix_item_id);
let montlyIndexDay = montly.days.indexOf(time);
montly.data[montlyIndexDay] = monthData;
AsyncStorage.setItem('weeklyMenu', JSON.stringify(weeklyMenu));
AsyncStorage.setItem('dailyData', JSON.stringify(dailyData));
AsyncStorage.setItem('montly', JSON.stringify(montly));
console.log('!update data:\n', weeklyMenu.week[day][meal]);
//i can see update my data
return {...state, weeklyMenu, dailyData, montlyData: montly};
case 'REMOVE_FOOD':
food = action.payload.food;
year = action.payload.year;
month = action.payload.month;
date = action.payload.date;
day = action.payload.day;
meal = action.payload.meal;
time = `${month}/${date}/${year}`;
let indexFood = weeklyMenu.week[day][meal].indexOf(food[0].food_name);
if (indexFood > -1) {
weeklyMenu.week[day][meal].splice(indexFood, 1); // 2nd parameter means remove one item only
weeklyMenu.foodId[day][meal].splice(indexFood, 1);
}
dailyData.protein -= food[0].nf_protein / 4;
dailyData.fat -= food[0].nf_total_fat / 4;
dailyData.carbohydrate -= food[0].nf_total_carbohydrate / 4;
dailyData.calories -= food[0].nf_calories / 4;
monthData = [
dailyData.protein,
dailyData.fat,
dailyData.carbohydrate,
dailyData.calories,
];
montlyIndexDay = montly.days.indexOf(time);
montly.data[montlyIndexDay] = monthData;
AsyncStorage.setItem('weeklyMenu', JSON.stringify(weeklyMenu));
AsyncStorage.setItem('dailyData', JSON.stringify(dailyData));
AsyncStorage.setItem('montly', JSON.stringify(montly));
console.log('!update data:\n', weeklyMenu.week[day][meal]);
//i can see update my data
return {...state, weeklyMenu, dailyData, montlyData: montly};
default:
return state;
}
}
Below is my app screen. When I get the weeklyMenu variable for the first time, the graph appears, but the pie graph does not change when there is a change in the weeklyMenu yield.
CodePudding user response:
Let's start in your component:
If you do
const selectorData = useSelector(item => item.userData);
const [userInfo, setUserInfo] = React.useState(selectorData);
you are using the selectorData
as the initial state to userInfo
. That means, no matter how often selectorData
changes later, userInfo
will never update.
You seem to kinda get around that by using the useEffect
, but seriously: why don't you just skip the local component state and just do
const userInfo = useSelector(item => item.userData);
?
But the real problem is in your reducer.
Let's put together what you do there:
let montly = state.montlyData;
...
montly.days.push(time);
Since montly
is just a reference to state.montlyData
, that is exactly the same as doing state.montlyData.days.push(time)
- which is something you are never allowed to do in legacy Redux reducers: you are not allowed to mutate the state.
The same goes for the montly.days.shift()
and most obviously the montly.data[montlyIndexDay] = monthData;
.
You would have to write a lot of complicated immutable logic to make this work in a legacy Redux reducer and honestly, it wouldn't help readability.
But now I have used the word "legacy" two times already, so this is probably the most important point:
You have probably being following outdated tutorials.
In modern Redux, as it is officially recommended by the Redux team, you should not be writing switch..case reducers any more since about 2019.
If you write a "modern Redux" createSlice
reducer, inside that you can mutate state as you please.
So my recommendation would be that you follow the official Redux tutorial to learn how to use modern Redux - since that is in general a lot easier to use and not suspectible to mutation bugs like you are experiencing right now.
For more information, please read Why Redux Toolkit is how to use Redux today.
CodePudding user response:
@phry
Thank you for your reply and the information you provided. For now, I made some improvements in the reducer in the form of state.montlyData.days.push(time)
. I will learn the modern version.
const selectorData = useSelector(item => item.userData);
const [userInfo, setUserInfo] = React.useState(selectorData);
Just const selectorData = useSelector(item => item.userData);
I wanted to try it with useEffect because I couldn't see the data change on the screen using the line. So I created a variable using useState but the result did not change as you know.