Using the results from useEffect - javascript

I have two calls to two seperate APIs I want to create a third result that is a combination of the first two. However I cannot access the variables.
export default function AutoServices() {
const [data, setData] = useState();
const [a, setA] = useState();
const [b, setB] = useState();
const [C, setC] = useState();
useEffect(() => {
FetchCurrentPage(result => setData(result.content.default));
FetchAutoServices(result => b(result.records));
FetchSetAccel("/locationsinit", result => setA(result.serviceGroups));
setC(AddDesc(a, b));
}, []);
function AddDesc(x, y) {
var result = x;
for (var i = 0; i < result.length; i++) {
for(var j = 0; j < y.length; j++)
{
if(result[i].slug === y[j].iconclass)
{
Object.assign(result[i], {Desc: y.content.default.teaser});
}
}
}
return result;
}
return (
<>
{data && (
<Helmet> ...........
The error in the browser is
Cannot read property 'length' of undefined referring to the first line of the for statement
I know that my code is returning json because when I inspect chrome I can see the json

State updates are asynchronous, which is why a is still undefined when you call AddDesc in the same useEffect closure. You could add a second useEffect with a and b as dependencies:
useEffect(() => {
FetchCurrentPage(({ content }) => setData(content.default));
FetchAutoServices(({ records }) => setB(records));
FetchSetAccel("/locationsinit", ({ serviceGroups }) => setA(serviceGroups));
}, []);
useEffect(() => {
if (a && b) setC(AddDesc(a, b));
}, [a, b]);

You are dealing with async code, so you'll have to wait on the first two to resolve. You can use Promise.all, and then do whatever with the result *potentially like:
useEffect(() => {
FetchCurrentPage(result => result.content.default)
const [a, b] = Promise.all([
FetchAutoServices(result => result.records),
FetchSetAccel("/locationsinit", result => result.serviceGroups);
]);
setC(AddDesc(a, b));},
[]);

Related

How to swap two signals in SolidJS

How do you swap these signals so that b = prev a and a = prev b?
const [a, setA] = createSignal(50)
const [b, setB] = createSignal(100)
function swapSignals() {}
We extract values into local variables before running the state updates otherwise they will end up having the same value, and wrap the update logic into batch to prevent multiple re-renders:
import { batch, createSignal } from 'solid-js';
import { render } from 'solid-js/web';
const App = () => {
const [a, setA] = createSignal(50)
const [b, setB] = createSignal(100)
const swapSignals = () => {
const x = a();
const y = b();
batch(() => {
setA(y);
setB(x);
});
};
return (
<div>
a: {a()} b: {b()}{` `}<button onClick={swapSignals}>Swap</button>
</div>
)
};
render(() => <App />, document.body);
https://playground.solidjs.com/anonymous/f744c2d3-8333-4f63-bf1e-d1e3665f81a2
That was easy than I thought
setA(aPrev => {
const bPrev = b()
setB(aPrev)
return bPrev
})

Array not able to be copied into new variable

while I am trying to develop my app, i keep getting the following error:
TypeError: Invalid attempt to spread non-iterable instance.
The error states a spread operator is being placed on a non-iterable but I am doing this on an array so it does not make sense to why I am receiving this error. I believe the error is occurring between these lines of code:
const Display = ({persons, setPersons, setFilterChecker, setErrorMessage, filter}) => {
const [counter, setCounter] = useState(0)
const [findNames, setFindNames] = useState([])
const [findNumbers, setFindNumbers] = useState([])
const copyOfNames = [...findNames]
const copyOfNumbers = [...findNumbers]
const copy = [...persons]
for (let j = 0; j < copy.length; j++) {
if ((copy[j].name).includes(filter)) {
setFindNames(copyOfNames.push(copy[j].name))
setFindNumbers(copyOfNumbers.push(copy[j].number))
}
}
However, here is the full code of Display.js which contains the above code:
import { useEffect, useState } from 'react'
import phoneService from '../services/information'
const handleDelete = (i, persons, setPersons, name2, setFilterChecker, setErrorMessage, setCounter, counter, findNames) => {
if (window.confirm(`delete ${name2} ?`)) {
const newArrayOfPeople = persons.filter(person => person.number !== findNames[i].number)
console.log(newArrayOfPeople)
const newArrayOfNames = newArrayOfPeople.map(person => person.name)
setFilterChecker(newArrayOfNames)
setPersons(newArrayOfPeople)
console.log(persons[i].id)
phoneService.remove(persons[i].id)
setErrorMessage(`You have successfully deleted ${name2} from the list.`)
setCounter(counter + 1)
}
}
const Display = ({persons, setPersons, setFilterChecker, setErrorMessage, filter}) => {
const [counter, setCounter] = useState(0)
const [findNames, setFindNames] = useState([])
const [findNumbers, setFindNumbers] = useState([])
const copyOfNames = [...findNames]
const copyOfNumbers = [...findNumbers]
const copy = [...persons]
for (let j = 0; j < copy.length; j++) {
if ((copy[j].name).includes(filter)) {
setFindNames(copyOfNames.push(copy[j].name))
setFindNumbers(copyOfNumbers.push(copy[j].number))
}
}
if (filter) {
return (
findNames.map((name, i) => <div id='parentContainer'><nobr key={name}>{name} {findNumbers[i]}</nobr> <button onClick={() => handleDelete(i, persons, setPersons, name, setFilterChecker, setErrorMessage, setCounter, counter, findNames)}>delete</button></div>)
)
} else {
return ''
}
}
export default Display
Why is this occurring if an array IS iterable?
I believe the error is occurring specifically with the variables copyOfNames and copyOfNumbers.
Array.push returns a new length of array (number), not array.
You should do something like
for (....) {
copyOfNames.push(copy[j].name)
copyOfNumbers.push(copy[j].number)
}
setFindNames(copyOfNames)
setFindNumbers(copyOfNumbers)
change
setFindNames(copyOfNames.push(copy[j].name))
setFindNumbers(copyOfNumbers.push(copy[j].number))
to
setFindNames(names => [...names, copy[j].name])
setFindNumbers(numbers => [...numbers, copy[j].number])

Why is my data not rendering appropriately?

I'm still struggling with React Natives rendering order. I'm fetching the API, then I filter this data and finally I'm manipulating the data. When I first load the app, it does not show the Data appropriately only when I'm saving within my code editor it shows up.
My simplified code:
const [data, setData] = useState([]);
const [sumPost, setSumPost] = useState(0);
const [sumProd, setSumProd] = useState(0);
useEffect(() => {
const unsubscribe = db.collection("Dates").where("projektName", "==", Projektname).onSnapshot(snapshot => (
setData(
snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
})))
))
return unsubscribe;
}, [])
const produktionsFilter = data.filter( x =>
x.data.jobtype == "Produktion"
);
const postFilter = data.filter( x =>
x.data.jobtype == "Postproduktion"
);
const terminFilter = data.filter( x =>
x.data.jobtype == "Termin"
);
let i;
const addPostProdTage = () => {
const post = [];
const prod = [];
for(i=0; i < postFilter.length; i++){
const p = postFilter[i].data.alleTage.length;
post.push(p)
}
for(i=0; i < produktionsFilter.length; i++){
const l = produktionsFilter[i].data.alleTage.length;
prod.push(l)
}
setSumPost(post.reduce(function(a, b){
return a + b;
}, 0));
setSumProd(prod.reduce(function(a, b){
return a + b;
}, 0));
}
useEffect(() => {
addPostProdTage();
}, [])
return(
<View>
<Text>{sumPost}</Text>
<Text>{sumProd}</Text>
</View>
)
sumProd should be 18 and sumPost should be 3. Right now it is showing 0 on both, because both states are empty arrays initially. It needs to some sort refresh.
I'm sure, there are more efficient ways to code this, but I need help to understand, why my data is not showing appropriately when I first load the app, because I'm running into this problem over and over again.
Thanks to all the advise I got on here, so for future reference this is how I solved this:
I filtered the data inside snapshot:
useEffect(() => {
const post = db
.collection("Dates")
.where("projektName", "==", Projektname)
.where("jobtype", "==", "Postproduktion")
.onSnapshot((snapshot) =>
setPost(
snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
)
);
return post;
}, []);
I had unnecessary steps to do my calculation. I could simplify this into a single function:
const revData = () => {
setSumPost(
post.reduce(function (prev, cur) {
return prev + cur.data.alleTage.length;
}, 0)
);
};
And finally, I had a useEffect to call that function after the data has been fetched using the dependency array:
useEffect(() => {
revData();
}, [post]);
You are creating local variables that go out of scope. You would be able to catch this error if you were using typescript instead of javascript.
You want to instead create state objects like this:
const [sumPost, setSumPost] = useState(0)
const [sumProd, setSumProd] = useState(0);
And then set the values of those objects as shown:
setSumPost(postproduktionsTage.reduce(function(a, b){
return a + b;
}, 0));
setSumProd(produktionsTage.reduce(function(a, b){
return a + b;
}, 0));
And then you can use it as you desire:
return(
<View>
<Text>{sumPost}</Text>
<Text>{sumProd}</Text>
</View>
)

having n states in react, assuming that n won't be received in props

How could I have n states in a React component
Assuming that the component won't receive this n value in any props, is something that it will get from a database
Using useState will create the state, setState for each pair, but I need n pairs
Rafael
JavaScript arrays doesn't have a fixed length.
You can do something like
const [arr, setArr] = useState([]);
And when you receive n values from database just set it to the array using setArr(values)
Now arr will be an array containing n elements retrieved from database. You can then iterate over it and render them as you wish.
As T J pointed out. You can use an array in state.
Or, another option is to map n Components for each item, therefore instantiating n states.
const Example = (props) => {
const [data, setData] = useState();
useEffect(() => {
// ...fetch data
// setData(data);
});
if (data === undefined) {
return null;
}
return data.map((data) => <Item data={data} />);
};
const Item = (props) => {
const [state, setState] = useState(props.data);
return <>Example</>;
};
Or if n is literally just a number, a count. Then you could do something like this.
const Example = (props) => {
const [count, setCount] = useState();
useEffect(() => {
// ...fetch count
// setCount(count);
});
if (count === undefined) {
return null;
}
const items = [];
for (var i = 1; i <= count; i++) {
items.push(<Item />);
}
return items;
};
const Item = (props) => {
const [state, setState] = useState();
return <>Example</>;
};

How to check when useState variable becomes available

I have useState variable that gets set based on a Promise that's resolved, how can I access the variable once it been setup
at the moment to get the correct values I have to use a setTimeout function, just wondering if there was a better way of doing that.
const FlagScreen = ({ t, i18n, history }) => {
const [flagAvailability, setFlagAvailability] = useState([]);
const [showFlags, setShowFlags] = useState([]);
useEffect(() => {
let flagsAvailable = [];
for (let [key, value] of Object.entries(flags)) {
if (key.indexOf(i18n.language) !== -1) {
for (const v of value) {
checkForAvailableAgent(`sales_${v}`, LINK_TO_STUDIO, SERVICE_ID)
.then(res => {
flagsAvailable[v] = res;
// Sets the flags availability i.e de: false, en: true
setFlagAvailability(flagsAvailable);
})
.catch(error => {
console.log("an error happened.");
});
}
}
}
}, [i18n.language]);
useEffect(() => {
//the value of flagAvailability is not available yet, I have to set a timeout function for
// 3 seconds for it to be available
console.log("flag availability: ", Object.entries(flagAvailability));
for (let [k, v] of Object.entries(flagAvailability)) {
console.log("key is: ", k);
if (v === true) {
setShowFlags(k);
}
}
}, [flagAvailability]);
}
<Container className="h-100">
<Row className="h-45 mt-5 text-center">
{ALL_STUDIOS_FLAGS.filter(item => {
return showFlags.includes(item);
}).map((item, index) => (
<Col key={index}>
<img
src={require(`../assets/flags/${item}.png`)}
alt={`${item} flag`}
/>
<span>{item.toUpperCase()}</span>
</Col>
))}
</Row>
any help would be appreicated,
The setFlagAvailability(flagsAvailable); in the first useEffect will only update the value of flagAvailability in second render
the second useEffect in the first render will only get the initial value of flagAvailability ( I know it's like a circle)
Every render has its own useEffect and useState
One of the solutions is to skip the first render in setShowFlags useEffect by creating a useRef flag like this
const ref = useRef(false)
const [flagAvailability, setFlagAvailability] = useState(true);
const [showFlags, setShowFlags] = useState(true);
useEffect(()=> {
if(ref.current){
...
setShowFlags();
ref.current = false;
}
},[flagAvailability])
useEffect(() => {
...
setFlagAvailability();
ref.current = true
},[i18n.language])
or you can simple change flagAvailability as useRef cause the refvalue will always be the new value.
const flagRef = useRef([]])
const [showFlags, setShowFlags] = useState(true);
useEffect(() => {
flagRef.current = flagsAvailable
},[i18n.language])
useEffect(()=> {
for (let [k, v] of Object.entries(flagRef.current)) {
setShowFlags();
}
},[showFlags])

Categories