Update state that depends on other calculated state in React-Hooks - reactjs

I want to update a state (data) that depends on other calculated state (server)
setServer(prevTweets =>
[...json, ...prevTweets].filter(
(e, i, arr) => i === arr.findIndex(t => t.tweetId === e.tweetId)
)
)
The data above will be used to set the state below (data) :
let totalPositive = 0;
let totalNegative = 0;
let totalNeutral = 0;
server.forEach(tweet => {
if(tweet.sentiment >0) totalPositive++;
if(tweet.sentiment < 0) totalNegative++;
if(tweet.sentiment ===0) totalNeutral++;
})
setData([
{ name: 'Positive', value: totalPositive },
{ name: 'Negative', value: totalNegative },
{ name: 'Neutral', value: totalNeutral },
])
Since it's asynchronous, the setData operation is always late. I know that I can use useEffect but apparently it will make an infinite loop and it's not right to use it in this case.

If you set the new data before you set the server you'd skip one render:
//still defaults to server so if you do't pass anything it;s still the same
const setNewData = (newServer = server) => {
const [
totalPositive,
totalNegative,
totalNeutral,
] = newServer.reduce(
([pos, neg, neu], { sentiment }) =>
sentiment > 0
? [pos + 1, neg, neu]
: sentiment < 0
? [pos, neg + 1, neu]
: [pos, neg, neu + 1],
[0, 0, 0]
);
setData([
{ name: 'Positive', value: totalPositive },
{ name: 'Negative', value: totalNegative },
{ name: 'Neutral', value: totalNeutral },
]);
};
setServer(prevTweets => {
const newServer = uniqueByTweetId([
...json,
...prevTweets,
]);
setNewData(newServer);
return newServer;
});
Unrelated to the question but could be important is that the way you get unique values could be improved. You could get unique values in one pass without having to call find index many times:
const uniqueBy = getter => arr => {
const items = new Map();
return arr.filter(item => {
const key = getter(item);
const ret = items.get(key);
items.set(key,true);
return !ret;
});
};
const data = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 1 },
{ id: 7 },
{ id: 1 },
{ id: 7 },
{ id: 8 },
{ id: 1 },
];
const uniqueById = uniqueBy(i => i.id);
console.log(uniqueById(data));

Related

Planning the calculation of progress percentages in the Progress Bar

I need to plan and implement the calculation of progress bar based on this object that i received from the backend :
due: {
coref: 1,
extract: 4,
keyconcepts: 1,
question-text-cmas: 1,
question-text-fibq: 1,
question-text-mcq: 1,
rank: 1,
summary-1: 1,
text: 1,
topic: 1
}
I already build the progress bar component, now i need to think of this impelementation.
This is the function that i started to implement :
const propertiesRef = useRef({
extract: { curr: 0, max: 15 },
keyconcepts: { curr: 0, max: 20 },
question: {
cmas: { curr: 0, max: 10 },
fibq: { crr: 0, max: 10 },
mcq: { curr: 0, max: 10 },
},
rank: { curr: 0, max: 5 },
summary: { curr: 0, max: 15 },
text: { curr: 0, max: 10 },
topic: { curr: 0, max: 5 },
allOver: 0,
}); // In this object i'll save the progress
const getProcess = async () => {
let conditionLoop = true;
do {
setTimeout(async () => {
await axios
.get(`Route`, { withCredentials: true }) //From here i get the due object
.then((res) => {
conditionLoop = res.data.due;
if (res?.data?.due) {
for (let key in propertiesRef.current) {
if (res.data.due.hasOwn(key)) {
console.log(key, res.data.due[key]);
}
if (res.data.due.hasOwn("question-text-cmas")) {
console.log(res.data.due);
}
if (res.data.due.hasOwn("question-text-fibq")) {
console.log(res.data.due);
}
if (res.data.due.hasOwn("question-text-mcq")) {
console.log(res.data.due);
}
}
} else {
propertiesRef.current.allOver = 1;
conditionloop=false;
}
console.log(propertiesRef.current);
});
}, 2000);
} while (conditionLoop);
};
This happens in my app while i generate some unit summary.
Important to say : When each property is done, it removed by the backend from the due object, and each property that have value 1 means that it still pending, when it's more than 1 it means that it is in progress.
Thanks in advance.
A simple minded solution would be to just remember the first due count, and compare it to the current one.
Something like this:
let totalProgress = useRef(null);
let progressFraction = useRef(null);
const progressFetchInterval = 2000;
const getProgress = async () => {
setTimeout(async () => {
await axios.get(`Route`, { withCredentials: true }).then((res) => {
fraction = getFraction(res);
getFraction.current = fraction;
if (fraction === 1) totalProgress.current = null;
else getProgress();
});
}, progressFetchInterval);
};
const getFraction = async (res) => {
// ... request validation code ...
const dueMap = res.data.due;
const dueCount = Object.values(dueMap).reduce((a, b) => a + b, 0);
if (totalProgress.current === null) totalProgress.current = dueCount;
return (totalProgress.current - dueCount) / totalProgress.current;
};

find a specifik obejct with arrays and print it out, react

I want to use find when clicking a button, I want to find an obejct (working in react)
let numbers = [
{ id: 0, namn: one, img: "../number/one" },
{ id: 1, namn: two, img: "../number/two" },
{ id: 2, namn: three, img: "../number/three" },
];
const [selected, setSelected] = useState({
id: 0,
name: "one",
img: "../number/one",
});
const count = useRef(0);
function next() {
if (count.current === 2) {
count.current = 0;
let found = numbers.find((number) => number.id === count.current);
} else {
count.current = count.current + 1;
let foundtwo = numbers.find((number) => number.id === count.current);
}
return (
<>
<img>{selected.img}</img>
namn: {selected.name}
</>
);
}
I can find it but I want to put it in a useState.
or somehow get it to show. how can I get found, and found two out in the return
the awswer was to put a
if( found === undefined){
console.log("error")
}else{
setSelected(found)
}

how to add object to empty list

I am trying to add object to list using setInterval to auto add the object every 1second but the output returns null as first index
Here my code below, I don't know where am getting it wrong
;
const [list, setList] = useState([])
const winners = [
{
name:'john',
price: '200'
},
{
name:'Micheal',
price: '230'
}
]
useEffect(() => {
const interval = setInterval(() => {
setSeconds(second => (second >= winners?.length ? 0 : second + 1));
}, 3000);
if(list?.length >=0){
const newList = list.concat(winners?.[`${seconds}`])
setList(newList)
}
if(winners.length === list?.length){
clearInterval(interval);
}
return () => clearInterval(interval)
}, [seconds]);
This is the output am getting // output
list = [
null,
{
name:'john',
price: '200'
},
null,
{
name:'Micheal',
price: '230'
}
]
**but I want this
list = [
{
name:'john',
price: '200'
},
{
name:'Micheal',
price: '230'
}
]
As far as I can read from your code, you could remove all null values from the Array, by applying a filter method on it.
Try this:
setList(newList.filter(item != null));

update State inside Map Function immediately React hooks

I tried with the below code but it is executing on the second attempt, I want to execute Alert on the Dropdown function, I am not rendering returnCount just using it to display an alert,
anyone knows the answer plz answer this, don't send any link, nothing is working out instead please write the answer
const [arrayList, setArrayList] = useState([
{ Id: 1, Name:'A', ItemDeliveryStatus:4 },
{ Id: 2, Name:'B', ItemDeliveryStatus:4 },
{ Id: 3, Name:'C', ItemDeliveryStatus:4 },
{ Id: 4, Name:'D', ItemDeliveryStatus:4 },
])
const [returnCount ,setReturnCount ]=useState(0)
//function on the picker, want to update returnCount immediately so that I can use it for below alert
function displayalertBox(){
arrayList.map(items =>
{
if(items.ItemDeliveryStatus=='4')
{
setReturnCount(prev=> prev+1)
}
})
if(returnCount==4)
{
alert('alert as returncount is equal to 4')
}
}
You need to do the desired functionality inside of an useEffect when using react hooks.
const [arrayList, setArrayList] = useState([{
Id: 1,
Name: 'A',
ItemDeliveryStatus: 4
},
{
Id: 2,
Name: 'B',
ItemDeliveryStatus: 4
},
{
Id: 3,
Name: 'C',
ItemDeliveryStatus: 4
},
{
Id: 4,
Name: 'D',
ItemDeliveryStatus: 4
},
])
const [returnCount, setReturnCount] = useState(0)
function displayalertBox() {
arrayList.map(items => {
if (items.ItemDeliveryStatus == '4') {
setReturnCount(prev => prev + 1)
}
})
}
// You cantry this too if needed.
function displayalertBox1() {
arrayList.map(items => {
if (items.ItemDeliveryStatus == '4') {
let count
setReturnCount(prev => {
count = prev + 1;
//since state update is guaranteed, you can try this too.
if (count === 4) {
alert('alert as returncount is equal to 4')
}
return count
})
}
})
}
useEffect(() => {
if (returnCount == 4) {
alert('alert as returncount is equal to 4')
}
}, [returnCount]);

How to update nested state properties in react using immutable way

I am trying to update children records based on api response in reducer. My current redux state is containing data in following format :
const container = {
data: [
{
id: "5e58d7bc1bbc71000118c0dc",
viewName: "Default",
children: [
{
id: "5e58d7bc1bbc71000118c0dd",
description: "child1",
quantity: 10
},
{
id: "5e58d7bc1bbc71000118c0ff",
description: "child2",
quantity: 20
},
{
id: "5e58d7bc1bbc71000118c0gg",
description: "child3",
quantity: 30
}
]
}
]
}
I am getting child1 data from api in following format :
{
id:"5e58d7bc1bbc71000118c0dd",
description:"child1",
quantity:20 // Quantity is updated
}
How can i update quantity for child1 in correct way ?
I am using immutable package.
https://www.npmjs.com/package/immutable
I honestly see no reason to use immutable.js, if you don't understand spread syntax or find it too verbose then you can use this helper.
const REMOVE = () => REMOVE;
const set = (object, path, callback) => {
const setKey = (current, key, value) => {
if (Array.isArray(current)) {
return value === REMOVE
? current.filter((_, i) => key !== i)
: current.map((c, i) => (i === key ? value : c));
}
return value === REMOVE
? Object.entries(current).reduce((result, [k, v]) => {
if (k !== key) {
result[k] = v;
}
return result;
}, {})
: { ...current, [key]: value };
};
const recur = (current, path) => {
if (path.length === 1) {
return setKey(
current,
path[0],
callback(current[path[0]])
);
}
return setKey(
current,
path[0],
recur(current[path[0]], path.slice(1))
);
};
return recur(object, path, callback);
};
const data = {
name: [{ hello: 'world', stay: true }, 4],
list: [1, 2, 3],
};
console.log(
'setting nested value',
set(data, ['name', 0, 'hello'], () => 'hello world')
.name[0].hello
);
console.log(
'doubling nested value',
set(data, ['name', 1], x => x * 2).name[1]
);
console.log(
'removing nested value',
set(data, ['name', 0, 'hello'], REMOVE).name[0]
);
console.log(
'adding to an array',
set(data, ['list'], v => [...v, 4]).list
);
console.log(
'mapping an array',
set(data, ['list'], v => v.map(v => v * 8)).list
);
console.log(
'data is not mutated',
JSON.stringify(data, undefined, 2)
);
You didn't post any code of how you save that data in state, did you use the immutable.js classes for it? If you did then say goodbye to redux dev tools and logging state to the console. Best to just leave it as data objects (serializable with JSON.stringify)

Resources