I have the following code :
export default function ProjectView({filteredProjects,revenueGroups}) {
const [projects,setProjects] = useState();
useEffect(() => {
const aap = filteredProjects.map(filteredObject => {
getProjectFields(filteredObject.projectId).then(res => {
filteredObject.rekt = res.data[0].id;
})
return filteredObject;
})
setProjects(aap);
},[filteredProjects])
And the rendered component :
return (
<div className='main'>
{projects.map(project => (
<div className='view-container' key={project._id}>
{console.log(project)}
</div>
))}
</div>
)
This works fine , when i console.log(project) like above it shows the following :
{
projectName: "privaye"
rekt: "project:1b1126ebb28a2154feaad60b7a7437df"
__proto__: Object
}
when i console.log(projectName) it shows the name, but when i console.log(project.rekt) it's undefined...
eventhough its there when i console.log(project)
EDITED
I didn't notice the was a promise inside :P
useEffect(() => {
fetchThings()
},[filteredProjects])
const fetchThings = async () => {
const promArr = filteredProjects.map(filteredObject => {
return getProjectFields(filteredProject.project.id)
})
const filteredObjects = await Promise.all(promArr)
const projectsMod = filteredObjects.map(fo => ({
...fo,
rekt: fo.data[0].id,
}))
}
Maybe an approach like this will help you with the asyncronous problem
console.log isn't rendering anything so maybe React doesn't try to refresh this part of the DOM when projects state is updated.
You can stringify the object to check the content of the variable inside the return
return (
<div className='main'>
{projects.map(project => (
<div className='view-container' key={project._id}>
<span>{ JSON.stringify(project) }</span>
</div>
))}
</div>
)
You are running promise, after this promise will return value you set rekt, but promise will work god knows when, and most probably you check value before this promise resolved.
getProjectFields(filteredObject.projectId).then(res => {
// THIS CODE CAN RUN AFTER RENDERING
filteredObject.rekt = res.data[0].id;
})
so first you wait till promises will complete and only then set new state
const ourPreciousPromises = filteredProjects.map(filteredObject =>
getProjectFields(filteredObject.projectId).then(res => {
filteredObject.rekt = res.data[0].id;
return {...filteredObject}
})
})
Promise.all(ourPreciousPromises).then(newObjects =>
setProjects(newObjects)
)
Related
I am playing around with an API that gets a list of Pokemon and corresponding data that looks like this.
export function SomePage() {
const [arr, setArray] = useState([]);
useEffect(() => {
fetchSomePokemon();
}, []);
function fetchSomePokemon() {
fetch('https://pokeapi.co/api/v2/pokemon?limit=5')
.then(response => response.json())
.then((pokemonList) => {
const someArray = [];
pokemonList.results.map(async (pokemon: { url: string; }) => {
someArray.push(await fetchData(pokemon))
})
setArray([...arr, someArray]);
})
}
async function fetchData(pokemon: { url: string; }) {
let url = pokemon.url
return await fetch(url).then(async res => await res.json())
}
console.log(arr);
return (
<div>
{arr[0]?.map((pokemon, index) => (
<div
key={index}
>
{pokemon.name}
</div>
))
}
</div>
);
}
The code works(kind of) however on the first render the map will display nothing even though the console.log outputs data. Only once the page has been refreshed will the correct data display. I have a feeling it's something to do with not handling promises correctly. Perhaps someone could help me out.
TIA
Expected output: Data populated on initial render(in this case, pokemon names will display)
The in-build map method on arrays in synchronous in nature. In fetchSomePokemon you need to return a promise from map callback function since you're writing async code in it.
Now items in array returned by pokemonList.results.map are promises. You need to use Promise.all on pokemonList.results.map and await it.
await Promise.all(pokemonList.results.map(async (pokemon: { url: string; }) => {
return fetchData.then(someArray.push(pokemon))
}));
On your first render, you don't have the data yet, so arr[0] doens't exist for you to .map on it, so it crashes. You need to check if the data is already there before mapping.
Using optional chaining, if there's no data it will not throw an error on your first render and it will render correctly when the data arrive and it re-renders.
...
return (
<div>
{arr[0]?.map((pokemon, index) => (
<div key={index}>{pokemon.name}</div>
))}
</div>
);
}
in
useEffect(() => { fetchSomePokemon(); }, []);
[] tells react there is no dependencies for this effect to happen,
read more here https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
One way to solve your issues is to await the data fetching in useEffect().
Here's a POC:
export function Page() {
const [pokemon, setPokemon] = useState([]);
// will fetch the pokemon on the first render
useEffect(() => {
async function fetchPokemon() {
// ... logic that fetches the pokemon
}
fetchPokemon();
}, []);
if (!pokemon.length) {
// you can return a spinner here
return null;
}
return (
<div>
{pokemon.map(item => {
// return an element
})}
</div>
);
}
I am trying to render text from an API call, text or numbers that are directly accesible from the axios.data object can render normally, nevertheless when inside the axios.data there is another object with its own properties I cannot render because an error shows, the error is 'undefined is not an object (evaluating 'coin.description.en')', over there description is an object; my code is
function SINGLE_COIN(props) {
const { id } = useParams()
console.log(id);
const SINGLE_API = `https://api.coingecko.com/api/v3/coins/${id}?tickers=true&market_data=true&community_data=true&developer_data=true&sparkline=true`
const [coin, setCoin] = useState({})
useEffect(() => {
axios
.get(SINGLE_API)
.then(res => {
setCoin(res.data)
console.log(res.data)
})
.catch(error => {
console.log(error)
})
}, [])
return (
<div>
<h2>{coin.name}</h2>
<div>{coin.coingecko_score}</div>
<div>{coin.liquidity_score}</div>
<div>{coin.description.en}</div>
<SINGLE_COIN_DATA coin={coin} />
</div>
)
}
Thanks!
For the initial render (data is not fetched yet), it will be empty. so nested property would be undefined.
so note the changes:
Example 1:
const [coin, setCoin] = useState(null);
..
return (
<div>
{coin ? (
<>
<h2>{coin.name}</h2>
<div>{coin.coingecko_score}</div>
<div>{coin.liquidity_score}</div>
<div>{coin.description.en}</div>
</>
) : null}
</div>
);
Example:2: Use the optional chaining while accessing nested property
return (
<div>
<h2>{coin?.name}</h2>
<div>{coin?.coingecko_score}</div>
<div>{coin?.liquidity_score}</div>
<div>{coin?.description?.en}</div>
</div>
);
And the complete code with : working example
export default function SINGLE_COIN() {
const { id } = useParams()
const SINGLE_API = `https://api.coingecko.com/api/v3/coins/${id}?tickers=true&market_data=true&community_data=true&developer_data=true&sparkline=true`;
const [coin, setCoin] = useState(null);
useEffect(() => {
axios
.get(SINGLE_API)
.then((res) => {
setCoin(res.data);
})
.catch((error) => {
console.log(error);
});
}, []);
return (
<div>
{coin ? (
<>
<h2>{coin.name}</h2>
<div>{coin.coingecko_score}</div>
<div>{coin.liquidity_score}</div>
<div>{coin.description.en}</div>
</>
) : null}
</div>
);
}
so when i do my login and i try to redirect to this page it appears me that error it must be something with useEffect i dont know
here is my code
useEffect(() => {
let canUpdate = true;
getVets().then((result) => canUpdate && setVets(result));
return function cleanup() {
canUpdate = false;
};
}, []);
const getVets = async () => {
const url = 'http://localhost:8080/all/vet';
const response = await fetch(url);
const data = await response.json();
setVets(data);
};
// const { appointmentType, animalID, room, hora, notes } = this.state;
return (
<React.Fragment>
<div class='title'>
<h5>2 médicos vetenários disponíveis</h5>
</div>
<div>
{vets.map((data) => (
<ObterMedicos
key={data.name}
name={data.name}
specialty={data.specialty}
/>
))}
</div>
</React.Fragment>
);
}
vets might not have data on the first render and when the code tries to execute map operation on it, it gets undefined.map, which is not allowed.
You can either set vets to empty array at the time of defining the state
const [vets,setVets] = useState([]);
or just check on vets using Optional chaning (?) before using the map function:
{vets?.map((data) => (
<ObterMedicos
key={data.name}
name={data.name}
specialty={data.specialty}
/>
))}
I am making a rather simple API call to (https://api.punkapi.com/v2/beers) and displaying fetched data in the component, but somehow the data is not getting displayed on the page, but when I console log it the data is displayed correctly.
const Comp= () => {
const [item, setItem] = React.useState([]);
React.useEffect(() => {
fetch('https://api.punkapi.com/v2/beers')
.then((res) => res.json())
.then((data) => {
setItem(data);
})
.catch((err) => {
console.log(err);
});
}, []);
return (
<div>
{item.map((beer) => {
const { name, tagline } = beer;
<p>{name}</p>;
console.log(name);
})}
</div>
);
};
Issue
You don't return anything from your item mapping. Return the JSX you want to render (i.e. return <p>{name}</p>) and don't forget to add a React key to the mapped elements. Below I've simplified to an implicit return.
You should also remove the console log from the render return as "render" methods are to be pure functions without side-effects, such as console logging. Either log the fetch result in the promise chain or use an useEffect hook to log the updated state.
useEffect(() => console.log(item), [item]);
...
return (
<div>
{item.map(({ id, name }) => (
<p key={id}>{name}</p>
))}
</div>
);
You need to return the value from inside .map method like:
return (<p>{name}</p>)
You need to return the JSX elements in your map.
const Comp= () => {
const [item, setItem] = React.useState([]);
React.useEffect(() => {
fetch('https://api.punkapi.com/v2/beers')
.then((res) => res.json())
.then((data) => {
setItem(data);
})
.catch((err) => {
console.log(err);
});
}, []);
return (
<div>
{item.map((beer) => {
const { name, tagline } = beer;
return <p>{name}</p>;
console.log(name);
})}
</div>
);
};
after a fetch if I click to some card I am able to populate an empty array.
I would like to pass it as a prop to a child component and I guess I am doing the right way, the problem occurs when within the children I am trying to console log it because I can not see any errors and the console.log is not printing anything
let shoppingCart = [];
const fetchProducts = async () => {
const data = await fetch(
"blablablablablab"
);
const products = await data.json();
setProducts(products);
console.log(products);
};
const handleShoppingCart = product => {
shoppingCart.push(product);
console.log(shoppingCart);
return shoppingCart;
};
Inside the return function I tried to check if the array was not empty, if was not undefined or if was not null but with the same result
{shoppingCart.length !== 0 ? (
<ShoppingCart parkingSlots={shoppingCart} />
) : null}
children component
const ShoppingCart = ({ parkingSlots }) => {
console.log(parkingSlots);
const parkingSlotsComponent = parkingSlots.map((parkingSlot, i) => {
// const { name } = parkingSlot;
return (
<div className="parking_details" key={i}>
{parkingSlot.name}
</div>
);
});
return <div className="checkout">{parkingSlotsComponent}</div>;
};
The data is in props.
When data is passed to child component via props, then it is part of props child component. Try below and see if you can console log the data.
const ShoppingCart = props => {
console.log(props.parkingSlots);
const parkingSlotsComponent = props.parkingSlots.map((parkingSlot, i) => {
// const { name } = parkingSlot;
return (
<div className="parking_details" key={i}>
{props.parkingSlot.name}
</div>
);
});
return <div className="checkout">{parkingSlotsComponent}</div>;
};