React - Populating First Select Option with Redux State - javascript

I have component League.js where there are 4 boxes containing League team details.
The team details are coming from this API => https://www.api-football.com/demo/api/v2/teams/league/${id}
When I click on each of the league boxes I am populating the Dropdown in my component Details.js
I won't get the team_id of the first team in the dropdown anytime I click on each of the League boxes in League.js in order to have the right calculation in a component called Stat.js. To do this I make a call request to this endpoint => https://www.api-football.com/demo/api/v2/statistics/${league}/${team}
So I need to pass league_id and team_id as a parameter, I can get both values correctly but I am doing something wrong on how to pass the first team_id of the team for each league.
These are the steps, I put only the relevant code. I am creating firstTeamStats state that I am dispatching
In my actions => index.js
export const RECEIVE_FIRST_TEAM_STATS = "RECEIVE_FIRST_TEAM_STATS";
export const receivedFirstTeamStats = json => ({
type: RECEIVE_FIRST_TEAM_STATS,
json: json
});
.get(`https://www.api-football.com/demo/api/v2/teams/league/${id}`)
.then(res => {
let teams = res.data.api.teams;
dispatch(receivedFirstTeamStats(teams[0].team_id));
})
in my reducer => index.js
case RECEIVE_FIRST_TEAM_STATS:
return {
...state,
firstTeamStats: action.json,
isTeamsDetailLoading: false
};
in my League.js component
import {getTeamsStats} from "../actions";
const mapStateToProps = state => ({
firstTeamStats: state.firstTeamStats
});
const mapDispatchToProps = {
getStats: getTeamsStats,
};
const onClick = (evt, id, firstTeamStats) => {
evt.preventDefault();
getDetail(id); \\ get the team names in the Detail.js component
getStats(id, firstTeamStats); \\ get the state value for the first team in the dropdown in Detail.js component
};
<a href={`#${item.league_id}`} onClick={(e) => onClick(e, item.league_id, firstTeamStats)}
Right now firstTeamStats in the onclick method above returns correctly the first team state value but of the existing league and not in the one where I click which is what I want.
However I can get this correctly in the Details.js Component, I have put {firstTeamStats} there in the demo where I have reproduced my case => https://codesandbox.io/s/romantic-solomon-6e0sb (Use CORS Unblock Chrome extension to see )
So the question is, how can i pass {firstTeamStats} correctly in League.js in the method onclick to the request getStats(id, firstTeamStats) where {firstTeamStats} is the state of my first team_id of the league?

Whenever you are making an API call, it is an asynchronous event. You should always await, before using its response in the next line.
So inside getDetail(), you are making an API call. But you haven't declared this method as async. so you are not waiting for it to complete and in the next line you are calling getStats() method with an old data. Thats why your app will always be a step behind, than your DOM events.
One way would be to use async|await and return the API response
from the function, instead of using dispatch.
You can also invoke the getTeamsStats() inside the getTeamsDetailById() after the api response is fetched, instead of invoking it inside OnClick()
event.
const onClick = (evt, id, firstTeamStats) => {
evt.preventDefault();
getDetail(id); \\ this is an async api call. you should have used await here.
getStats(id, firstTeamStats); \\ It will execute before the new stats are fetched
};
In Stats.js, you should not be strictly validating these fields to show Statistics... these are only numbers.. can also be 0 at times. so please remove these checks.
if (
teamsStatsWinHome &&
teamsStatsWinAway &&
teamsStatsDrawHome &&
teamsStatsDrawAway &&
teamsStatsLoseHome &&
teamsStatsLoseAway
) {
I have a working instance of your application... check below.
https://codesandbox.io/s/charming-dewdney-zke2t
Let me know, if you need anything.

Related

I want to access my state variable from one component to other

I have a react query which writes the state variable- follower, and I want to access this variable in other component to find its .length can someone tell me how do I do it
const ModalFollower = ({profile}) => {
const [follower,setFollower] = useState([])
const {
data: followerName,
isLoading: followerLoading,
isFetching: followerFetching
} = useQuery(["invitations", profile?.id], () => {
getFollowers(profile?.id).then((response) => {
if (response) {
setFollower(response);
}
});
});
return(
{
!followerLoading && (
follower.map((e) => {
return(<>
<p>{e.requested_profile.Userlink}</p>
</>}
)
}
)
I want to access the length of follower in some other component
There is no need to copy data from react-query to local state, because react-query is a full-blown state manager for server state. As long as you use the same query key, you will get data from its cache. This is best abstracted away in custom hooks.
Please be aware that with the default values, you will get a "background refetch" if a new component mount, so you will see two network requests if you use it twice. That might look confusing at first, but it is intended, as it is not react-query's primary goal to reduce network requests, but to keep your data on the screen as up-to-date as possible. So when a new component mounts that uses a query, you'll get the stale data from the cache immediately, and then a background refetch will be done. This procedure is called stale-while-revalidate.
The best way to customize this behaviour is to set the staleTime property to tell react-query how long your resource is "valid". For that time, you will only get data from the cache if available. I've written about this topic in my blog here: React Query as a State Manager.
React Query also provides selectors, so if your second component is only interested in the length, this is what my code would look like:
const useInvitations = (profile, select) =>
useQuery(
["invitations", profile?.id],
() => getFollowers(profile?.id),
{
enabled: !!profile?.id
select
}
)
Note that I also added the enabled property because apparently, profile can be undefined and you likely wouldn't want to start fetching without that id.
Now we can call this in our main component:
const ModalFollower = ({profile}) => {
const { data } = useInvitations(profile)
}
and data will contain the result once the promise resolves.
In another component where we only want the length, we can do:
const { data } = useInvitations(profile, invitations => invitations.length)
and data will be of type number and you will only be subscribed to length changes. This works similar to redux selectors.

Invalid hook call React while fetching data from an API

passing country full name at onClick event.
here is error
import React,{useState,useEffect} from 'react'; //
import axios from 'axios';
export const Country = (name) => {
const [country, setCountry] = useState([]);
const requestCountry = (name) => {
axios(`https://restcountries.eu/rest/v2/name/${name}?fullText=true`)
.then((res) =>
// Success handling
setCountry(res.data))
.catch((error) => {
// Error handling
console.error(error.message);
});
}
requestCountry(name)
}
Here is Source of Code Click here to see code
Hooks can be only used inside a Functional Component and not a normal function.
Seems like you are trying to call a Functional Component like normal function with an argument like below.
onClick={() => Country(data.name)}
Instead what you might want to do is, show a list of buttons with country names and then when one of the button is clicked, call a handler function which is the axios API call and then show the response country details or do whatever that you want with those detail data.
To do that, you need to save those responded country detail into a React state.
If country detail exists, show the details. If not, show the list.
So, I forked your codesandbox and edit it like this.
https://codesandbox.io/s/country-data-i479e?file=/src/App.js
Well from the error I can see that you have put the Country call inside a event handler onClick.
The truth is that hooks can not be called inside event listeners. If you need to change the state inside a listener you can do that but you will need to call useState outside of a listener and then call setState wherever you need.
That is because React uses order in which you call hooks to remember how execute your component in subsequent calls.
const [state, setState] = useState();
const onClick = () => {
setState(...);
} ;
As the previous answers have mentioned, you can use hooks only at functional level and not inside a handler.
You just need to move your hook a level up and pass it to your function.
Also, as you're not returning anything from the Country function, there's no need to import "React".
I have modified the code: https://codesandbox.io/s/quiet-night-7cdvi
Check console (Added a useEffect in Country.js just for logging, you can remove it).
Some changed done in your code. Here is the link to view code. https://codesandbox.io/s/practical-dust-lk0x7?file=/src/country.js

React - Unable to setState inside a Promise then

I'm trying to set the state inside a then() of a Promise, but the state value is not getting saved and is not accessible outside of the then().
Below is the UPDATED code:
handleSelect = location => {
this.setState({ location });
geocodeByAddress(
location
)
.then(results => getLatLng(results[0]))
.then(latLng => {
this.setState({
location_latitude: latLng.lat,
location_longitude: latLng.lng,
})
console.log(this.state.location_latitude); // returns the correct value
})
console.log(this.state.location_latitude); // returns null (the originally declared value)
}
If I console.log(this.state.location_latitude) I get a null value. But if I console.log inside the then() block, I get the correct value. How do I set the state in this case?
UPDATED explanation:
To give a better context to the whole logic here: I'm using a Google Places Autocomplete component which allows the user to select a location from the dropdown. The above method handleSelect is called when the user selects a location from the dropdown. I then extract the latitude and longitude of the place selected by the user using the geocodeByAddress method, which is a promise. I need to then retrieve the latitude and longitude and set the state accordingly, which is later used to send to the database. I can't submit the value to the database yet because there are other form inputs that the user needs to fill (which are also stored in the state) and once he clicks on Submit, I'll be submitting all the state elements in a single call to the backend. So, there is nothing I want to update with componentDidMount() or nothing is getting updated yet to use componentDidUpdate(). In any case, I can only store the state value inside the then of geocodeByAddress (please correct me if I'm wrong here). So, I HAVE to be able to modify the state value inside the then block.
The thing is when you set-state you can't see the state result immediately after that. That's why the console.log returns null. Make sure your console.log is in the render block. This is because set-state actions are asynchronous and are batched for performance gains. This is explained in documentation of setState.
Take a look at this => Why is setState in reactjs Async instead of Sync?
You need to bind the function context to current class/ component. Try something like below. Create a function like "callSetState" bind it with this and while calling in then just pass the values as parameters to it.
import React, { Component } from 'react';
class Test extends Component{
constructor() {
super()
this.callSetState = this.callSetState.bind(this);
}
callSetState(latitude, longitude) {
this.setState({
location_latitude: latitude,
location_longitude: longitude,
})
}
/* Call this func in your class and call callSetState which is already bound to your component in constructor */
// geocodeByAddress(location)
// .then(results => getLatLng(results[0]))
// .then(latLng => this.callSetState(latLng.lat, latLng.lng));
}
This is clearly an issue with your data call not react.
Your react code looks fine but I suggest spitting the code.
1) service.js file - this does your query and returns data or returns error.
2) location component, componentDidMount() or useEffect(() => {}, []) gets the data and updates state/useState()
—
To give you a code snippet I need to you to provide a console log of latLang & results.
Many thanks 👌🏻

Issues with asynchronous nature of redux in React?

I'm pulling data into one of my parent components and then using various filter statements which are based on user choices from select boxes. I'm then calling an action which simply stores that filtered data based on the users search into global state so that my child components can access them.
One of my child components is supposed to render the results but what is happening is the results being rendered are lagging one action behind. I've encountered similar issues when using set state and my solution then was to use a callback but I'm not exactly sure how to go about dealing with this issue in this situation with redux.
The wordpress.get is just named import of axios config.
componentDidMount = async () => {
const response = await wordpress.get(
"*********************/api/wp/v2/variants?per_page=100"
);
this.props.fetchData(response);
const data = []
response.data.forEach(ele => {
data.push(ele)
})
this.props.sendFilteredView(data);
};
handleChange = () => {
this.preBuiltFiltering();
};
I've left out pre-built filtering because its long and excessive, all it does is run the filter based on the users choices and then dispatches the this.props.sendFilteredView action with the filtered data set as the argument. The action just returns the payload.
I then am rendering the results of the filter in a child component by accessing the global state (I also tried just passing it directly through props, same issue).
It’s an async function, you’re using a callback after the forEach with data.
So you need to wait forEach been completed.
Try to use await before forEach.
componentDidMount = async () => {
const response = await wordpress.get(
"*********************/api/wp/v2/variants?per_page=100"
);
this.props.fetchData(response);
const data = []
await response.data.forEach(ele => {
data.push(ele)
})
this.props.sendFilteredView(data);
};
handleChange = () => {
this.preBuiltFiltering();
};

Is it necessary for action creators to return an action ?

I have an action creator which takes in an id and a callback function. It sends a request to server to do some stuff and returns a dummy action. All I want to do here is call the callback function and exit, because that dummy action has no use to me for example liking a post. I just need the server to know that a user liked a post, I don't some state changes, I can change the like button state via callback. So my question is do we require an action creator to return an action. Here is my code
export const deleteProduct = (id,cb) => async (dispatch) => {
let res = await axios.delete(`${BASE_URL}/api/v1/product/${id}`,
{withCredentials:true})
cb();
dispatch({type: DUMMY, payload : res.data});
};
After I am deleting a product I can just hide that product from list when the callback is fired. Do I need to call the dispatch explicitly or is it optional. ? I am using Redux-thunk for handling axios promises.
In redux-thunk dispatching is completely optional. In fact, one of it's main motivations is to provide conditional dispatches.
So yeah, you can totally just do:
export const deleteProduct = (id,cb) => async () => {
let res = await axios.delete(`${BASE_URL}/api/v1/product/${id}`,
{withCredentials:true})
cb();
};
However, I believe this is breaking one of the main concepts of redux, which is unidirectional data flow. The concept is that the data should only flow in one direction. The view dispatches an action to update the store to trigger render to update the view. The view should not dispatch and action and rerender from the result.
Basically, If you want to hide the item from the list after it is deleted, instead of doing this in a callback, you should dispatch an action to update the data in the store and let react-redux rerender the component without the deleted item in it.

Categories

Resources