Display data from API - reactjs

i have the following code in parent.js file which gets data from API using axios which is working fine
//Parent.js
componentDidMount() {
axios.get('URL', {
method: 'GET',
headers: {
'key': 'apikeygoeshere'
}
})
.then((response) => {
this.success(response);
})
}
successShow(response) {
this.setState({
person: response.data.data.Table
});
}
render() {
return (
<div class="row">
{this.state.person.map(results => (
<h5>{results.first_name}</h5>
)
)
}
and the above code display data from api perfect. i want to display api data in child component instead of displaying data from json file. In child component i have the following code which display data from local.json file
//

The issue is in the successShow function the you cannot change the array state value like that. According to the answer from here :
Correct modification of state arrays in ReactJS
You can update the state like this:
this.setState(prevState => ({
person: [...prevState.person,response.data.data.Table]
});
or
this.setState({
person: this.state.person.concat([response.data.data.Table])
});

Try using
const data = this.state.person && this.state.person.find(item => item.id === id);
const relatedTo = this.state.person && this.state.person.filter( item => item.manager_id === data.manager_id && item.id !== data.id );

Related

React component uses old data from previous API call

I am using React Query to fetch data from an API I have built. The component is rendering the old data from the previous api call and not updating with new the data from the new api call.
The new data is only rendering when I refresh the page.
Component:
export const ProfilePageStats = (props: {
user: User;
id: number;
}) => {
const { chatId } = useParams();
const { status: subscribeStatus, data: subscribeData } =
useSubscriptionsWithType(
chatId ? chatId : "",
props.id,
props.user.id,
"SUBSCRIBE"
);
const { status: unsubscribeStatus, data: unsubscribeData } =
useSubscriptionsWithType(
chatId ? chatId : "",
props.id,
props.user.id,
"UNSUBSCRIBE"
);
if (unsubscribeStatus == "success" && subscribeStatus == "success") {
console.log("Working", unsubscribeData);
return (
<ProfilePageStatsWithData
user={props.user}
subscribed={Object.keys(subscribeData).length}
unsubscribed={Object.keys(unsubscribeData).length}
/>
);
}
if (unsubscribeStatus == "error" && subscribeStatus == "error") {
console.log("error");
return <ProfilePageStatsLoading />;
}
if (unsubscribeStatus == "loading" && subscribeStatus == "loading") {
console.log("loading");
return <ProfilePageStatsLoading />;
}
return <ProfilePageStatsLoading />;
};
export const useSubscriptionsWithType = (
chatId: string,
id: number,
userId: number,
type: string
) => {
return useQuery(
["subscriptionsWithType"],
async () => {
const { data } = await api.get(
`${chatId}/subscriptions/${id}/${userId}?type=${type}`
);
return data;
},
{
enabled: chatId > 0 && userId > 0,
refetchOnWindowFocus: false,
}
);
};
The component should update to show the new user values but shows the previous user values. If I click out and select a different user entirely it then shows the values for the previously clicked user.
I can see that React Query is fetching with the correct values for the query but the component still renders the old user data?
It turns out that the fetchStatus value is changing to "fetching" but it not actually calling the api. Hence, why its only using the old values?
Your key part of the useQuery is what tells the hook when to update.
You only use ["subscriptionsWithType"] as key, so it will never know that you need to refetch something.
If you add userId there, it will update when that changes.
So, using
return useQuery(
["subscriptionsWithType", userId],
async () => {
...
will work.
It is likely, that you want all the params, that you use in the url, to be added there.
I solved it by adding a useEffect and refetching based on the changing user id.
useEffect(() => {
refetch();
}, [props.user.id]);

onClick like button likes all objects in array React JS

I'm super new to web development but I'm trying to implement a like button to an API array of the mars rover images in react js. The problem I'm having is when I click the like button for one of my images all of the like buttons click. I've tried using id's, classNames, and keys for my button but nothing seems to be working. Any help would be appreciated :)
class Rover extends React.Component {
state = {
loading: true,
images: [],
};
async componentDidMount() {
const url = 'https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?sol=1000&page=2&api_key='
const response = await fetch(url);
const data= await response.json();
this.setState({ images: data.photos, loading: false });
console.log(this.state);
}
render() {
if (this.state.loading) {
return <div>loading...</div>;
}
if (!this.state.images) {
return <div>didn't find image</div>
}
return (
<IconContext.Provider value={{ color: '#a9b3c1', size: 64 }}>
<RoverSection>
<RoverWrapper>
<RoverHeading>Mars Rover</RoverHeading>
<div>
{this.state.images.map((image, idx) => (
<div className={`some-image-${idx}`} key={`some-image${idx}`}>
<RoverContainer>
<RoverCard to='/'>
<RoverCardInfo>
<RoverCardID>Photo ID: {image.id}</RoverCardID>
<RoverCardFeatures>
<Img src={image.img_src} />
<RoverCardFeature>{image.rover.name} Rover</RoverCardFeature>
<RoverCardFeature>{image.camera.full_name}</RoverCardFeature>
<RoverCardFeature>{image.earth_date}</RoverCardFeature>
</RoverCardFeatures>
<Button primary id={image.id} className={`some-button-${idx}`} onClick={() => {
this.setState({
isLiked: !this.state.isLiked
})
}}>{this.state.isLiked ? <FaHeart/> : <FaRegHeart/>}</Button>
</RoverCardInfo>
</RoverCard>
</RoverContainer>
</div>
))}
</div>
</RoverWrapper>
</RoverSection>
</IconContext.Provider>
);
}
}
export default Rover
You are mapping through all of the images (you have an array of images); However, you do not have an array for your likes you just have a single boolean value. You need to create an array for your likes of equal size to your image array and then use the same index (you can get the current index in your map (you already did an named it idx))
Your initial like state should be similar to:
async componentDidMount() {
const url = 'https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?sol=1000&page=2&api_key='
const response = await fetch(url);
const data= await response.json();
this.setState({ images: data.photos, isLiked:new Array(data.photos.length).fill(false), loading: false });
console.log(this.state);
}
and then in you component
this.setState((current)=>{
const newState={...current};
const isLiked = newState.isLiked;
isLiked[idx] = !isLiked[idx];
return {
...newState,
isLiked:[...isLiked]
}
})
and
{this.state.isLiked[idx] ? <FaHeart/> : <FaRegHeart/>}
lets say this is your image array:
this.state.images = [{id : 0, img : "img1"} ,{id : 1 , img : "img2"} ,{id : 2, img : "img3"} ]
now say you want to like img1 , when you click like on img1, dont you think that you need to only save img1 as liked image. you can do it by modifying the images array object. if you dont want to change data in your original image object,you can keep another array/object map with id and their like status ( you can keep all or you can only push the liked ones.
now when you click on any like button on image , call the function ( also bind it in constructor)
onClick = {(e)=> {this.handleLikeClick(image)}}
and in function handleLikeClick
handleLikeClick(image){
let copyImages = [...this.state.images]
this.state.images.forEach((img) => {
if(img.id === image.id) {
img.liked= !img.liked;
}
});
this.setState({
images :copyImages
})
}
now whenever you are in render , instead of just checking isLIked, you can check the liked property in image object.

setState not returned from render when using Axios

I'm using axios to get data from an endpoint. I'm trying to store this data inside the state of my React component, but I keep getting this error:
Error: Results(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
I've struggled with many approaches: arrow functions etc., but without luck.
export default class Map extends Component {
constructor() {
super();
this.state = {
fillColor: {},
selectedCounty: "",
dbResponse: null,
};
}
getCounty(e) {
axios.get("/getWeatherData?county=" + e.target.id)
.then((response) => {
this.setState(prevState => {
let fillColor = {...prevState.fillColor};
fillColor[prevState.selectedCounty] = '#81AC8B';
fillColor[e.target.id] = '#425957';
const selectedCounty = e.target.id;
const dbResponse = response.data;
return { dbResponse, selectedCounty, fillColor };
})
}).catch((error) => {
console.log('Could not connect to the backend');
console.log(error)
});
}
render() {
return (
<div id="map">
<svg>big svg file</svg>
{this.state.selectedCounty ? <Results/> : null}
</div>
)
}
I need to set the state using prevState in order to update the fillColor dictionary.
Should this be expected? Is there a workaround?

useState referring to stale value

I have a keeper app where I am adding notes and storing them in database. When I make a post request to the server, I am trying to fetch the _id from database, which will eventually help me to later delete the note ( if needed).
Here is my jsx file
function CreateMessage(props) {
const [currentGuest, setCurrentGuest] = useState({
guestName: '',
guestMessage: '',
id:''
});
function handleMessages(event) {
const {name, value} = event.target;
setCurrentGuest(prevGuest => {
return {
...prevGuest,
[name] : value
};
});
}
function submitMessage(event) {
//props.onAdd(currentGuest);
const params = {
guestName: currentGuest.guestName,
guestMessage: currentGuest.guestMessage,
}
axios
.post("http://localhost:8000/notes", params)
.then(res => {
console.log("The response is"+res.data._id);
console.log(res.status);
setCurrentGuest(prevGuest => {
console.log(res.data._id)
return {
...prevGuest,
id: res.data._id
};
});
console.log(currentGuest);
})
event.preventDefault();
}
return (
<div>
<form>
<input
name="guestName"
placeholder="Guest Name"
value={currentGuest.guestName}
onChange={handleMessages}
/>
<textarea
name="guestMessage"
placeholder="Write a Message"
rows="3"
value={currentGuest.guestMessage}
onChange={handleMessages}
/>
<button onClick={submitMessage}>Add</button>
</form>
</div>
);
}
The id is properly being fetched and displayed in ```console.log("The response is"+res.data._id"). But on first submit, the is always empty and stale id gets attached to the currentGuest object on next submit
function submitMessage(event) {
//props.onAdd(currentGuest);
const params = {
guestName: currentGuest.guestName,
guestMessage: currentGuest.guestMessage,
}
axios
.post("http://localhost:8000/notes", params)
.then(res => {
console.log("The response is"+res.data._id);
console.log(res.status);
setCurrentGuest(prevGuest => {
console.log(res.data._id)
return {
...prevGuest,
id: res.data._id
};
});
console.log(currentGuest);
})
event.preventDefault();
}
In the above snippet, after getting the response you're correctly changing the state but the problem is with where you're checking the changed state(console.log(currentGuest)). You're basically logging before the state is changed.
You can use useEffect hook and log the state inside it. This runs every time the currentGuest Changes.
useEffect(() => {
console.log(currentGuest)
}, [currentGuest])
Update
You can use the modified currentGuest inside the useEffect hook:
useEffect(() => {
console.log(currentGuest)
if(currentGuest.id) {
props.onAdd(currentGuest);
// You can also reset the state here as follows
setCurrentGuest({
guestName: '',
guestMessage: '',
id:''
});
}
}, [currentGuest]) // You might need to add the necessary dependencies to this array.

keep old content loaded previously in react pagination

I'm new using react.
I have an array from php.
React is getting it using axios to print some posts.
I'd like to show 10 posts and when user scroll down I get more 10 posts.
It is working ok. The problem is react is overwriting my old posts from the screen, I'd to keep all posts, not only the new ones loaded... Any ideas why react is only keeping the new ones?
my return:
return (
<>
{posts.map((post, index) => {
if (posts.length === index + 1) {
number = post.id;
return <div className={classes.post} ref={lastPostElementRef} key={post.id}>{post.titulo}</div>
}
else {
return <div className={classes.post} key={post.id}>{post.titulo}</div>
}
})}
<div>{loading && 'Loading...'}</div>
<div>{error && 'Error'}</div>
</>
)
my axios:
axios({
method: 'GET',
url: 'http://localhost/posts.php',
params: { q: query, page: pageNumber },
cancelToken: new axios.CancelToken(c => cancel = c)
}).then(res => {
setPosts(prevPosts => {
return res.data
})
if(res.data.length < 10) {
setHasMore(false); // total pagination items 10
}
else {
setHasMore(true);
}
thanks.
When you set the state in functional component, React will replace the previous state.
When you update a state variable, unlike this.setState in a component class, the function returned by useState does not automatically merge update objects, it replaces them.
Solution : You have to add the old state and the new state in a new array and then set this as the new state. You could use spread syntax to copy the old and the new state.
Example:
axios({
method: 'GET',
url: 'http://localhost/posts.php',
params: { q: query, page: pageNumber },
cancelToken: new axios.CancelToken(c => cancel = c)
}).then(res => {
setPosts(prevPosts => {
return [...prevPosts, ...res.data, ]
})
if (res.data.length < 10) {
setHasMore(false); // total pagination items 10
}
else {
setHasMore(true);
}
you need to add newly fetched results to current state;
Use this;
return [...prevPosts ,...res.data]
instead of
return res.data

Resources