Rendering only the specific component on Select option change. React - javascript

I have created a react app which will fetch the api using the values from the url params. which are modified using navigate prop without page refresh.
Here is the code.
const App = () => {
const [itemData, setItemData] = useState({});
const [itemError, setItemError] = useState({});
const [additionalData, setAdditionalData] = useState({});
const [additionalError, setAdditionalError] = useState({});
const [isLoading, setIsLoading] = useState(false);
const [showTrailer, setShowTrailer] = useState(false);
const [trailer, setTrailer] = useState({});
const [trailerError, setTrailerError] = useState({});
const [group, setGroup] = useState([])
const backend_url = process.env.REACT_APP_BACKEND;
const handleCloseTrailer = () => setShowTrailer(false);
const handleShowTrailer = () => setShowTrailer(true);
const location = useLocation();
const id = location.pathname.split("/")[2];
const [searchParams, setSearchParams] = useSearchParams();
const [people, setPeople] = useState([]);
const [groupId, setGroupId] = useState(searchParams.get("group_id"));
const navigate = useNavigate();
function handleChange(value) {
navigate(`?group_id=${value}`);
}
useEffect(() => {
const fetchMainApi = () => {
setIsLoading(true)
axios.get(`${backend_url}/api/v1/metadata?id=${id}`)
.then(function(response) {
if(response.data.content.apiId !== 'undefined') {
axios.get("API_URL")
.then(function (response) {
setAdditionalData(response.data);
})
.catch(function (error) {
setAdditionalError(error);
})
}
if(itemData && (itemData.apiId !== 'null' || 'undefined')) {
axios.get("API_URL")
.then(function(response) {
setTrailer(response.data)
})
.catch(function(error) {
setTrailerError(error)
})
}
if(type === "cat" && itemData.children) {
setGroup(itemData.children)
}
if(type === "cat" && itemData.children)
axios.get("API_URL" + groupId)
.then(function (response) {
setPeople(response.data.content.children);
})
.catch(function (error) {
console.log(error);
});
setItemData(response.data.content)
})
.catch(function(error) {
setItemError(error)
})
setIsLoading(false)
}
fetchMainApi()
}, [backend_url,id,type,itemData.apiId,itemData.api])
return (
<>
<Form.Select onChange={event => handleChange(event.target.value)} aria-label="Default select example">
<option>Group All</option>
{cluster.map((person, index) => (
<option key={guid()} value={group.id}>{group.name}</option>
))}
</Form.Select>
<People people={people}/>
</>
);
};
export default App;
Here is the People component
const People = ({people}) => {
return (
<Row className="m-2 pt-2">
<h2 className="color-white">People</h2>
{people && people.length > 0 && (people.map((people, index) => (
<Col key={index} className="p-lg-4 p-sm-3" xs={12} sm={6} md={4} lg={3} xl={3}>
....
</Col>
)))}
{ (!people || people.length === 0) && (<h5 className="color-white">No Persons Found</h5>) }
</Row>
);
};
export default People;
Working
The select menu updates the query param and then the value of param is taken inside useEffect hook when then provides the data.
Every thing works well but the problem is to update the data inside the component i need to refresh the page when then works as expected.
Is there a way to change or update only the people component without a page refresh.

Related

Reload updated Table once a column is removed

this is a component that retrieve data from API and generate a table. One button in a column for removing the row opens a Modal.
For avoiding in first component render to trigger api.delete request, an useRef set as false in second useEffect
Modal's Delete button return row info in deleteHandler function which successfully trigger api.delete request on confirmation, remove user in backend however table is not reloading.
Once row is removed expected result is triggering api.get request and display table now updated and without row removed.
In order to get that result I tried with const [reload, setReload] = useState(false); state which introduce another dependency to both userEffect
reload state effectively reload table data updated however it cause also that api.delete request trigger with const ida undefined. Below component script it can find useEffect with my tried.
Any hint or possible solution is appreciated
import React, { Fragment, useEffect, useState, useRef } from "react";
... other imports ...
export default function AllUsers() {
const api = useApi();
const [userData, setUserData] = useState();
const [showModal, setShowModal] = useState(false);
const navigate = useNavigate();
const [modalMessage, setModalMessage] = useState();
const [removeUserId, setRemoveUserId] = useState();
const [ida, setIda] = useState();
let effectRan = useRef(false);
const [reload, setReload] = useState(false);
useEffect(() => {
(async () => {
const response = await api.get("/admin/users");
if (response.ok) {
setUserData(response.body);
} else {
setUserData(null);
}
})();
}, [api]);
useEffect(() => {
if (effectRan.current) {
// console.log("effect run");
(async () => {
const response = await api.delete("/admin/users", {
ida: ida,
});
if (response.ok && response.status === 204) {
console.log(response);
} else {
console.log(response.body.errors);
}
})();
}
return () => (effectRan.current = true);
}, [api, ida]);
const handleEditClick = (e, rowIndex) => {
// console.log("user/" + rowIndex.username);
navigate("/user/" + rowIndex.username, [navigate]);
};
const handleRemoveClick = (e, rowIndex) => {
// console.log([rowIndex]);
setShowModal(true);
setModalMessage({
title: `Remove ${rowIndex.username}`,
message: `User's email to remove ${rowIndex.email}`,
});
setRemoveUserId(rowIndex.id);
};
const closeHandler = () => {
setShowModal(false);
};
const deleteHandler = () => {
// console.log(removeUserId);
setIda(removeUserId);
setShowModal(false);
};
// console.log(ida, idb);
return (
<Fragment>
{showModal && (
<BtModal
show={showModal}
title={modalMessage.title}
message={modalMessage.message}
handleClose={closeHandler}
onConfirm={deleteHandler}
/>
)}
<Body>
<h1>User Table</h1>
{userData === undefined ? (
<Spinner animation="border" />
) : (
<>
{userData === null ? (
<p>Could not retrieve users.</p>
) : userData.length === 0 ? (
<p>There are not users in system</p>
) : (
<UserListTable
newData={userData}
handleEditClick={handleEditClick}
handleRemoveClick={handleRemoveClick}
/>
)}
</>
)}
</Body>
</Fragment>
);
}
useEffect updated with reload state:
useEffect(() => {
(async () => {
const response = await api.get("/admin/users");
if (response.ok) {
setUserData(response.body);
effectRan.current = false;
} else {
setUserData(null);
}
})();
}, [api, reload]);
useEffect(() => {
if (effectRan.current) {
// console.log("effect run");
(async () => {
const response = await api.delete("/admin/users", {
ida: ida,
});
if (response.ok && response.status === 204) {
console.log(response);
setReload(!reload);
} else {
console.log(response.body.errors);
}
})();
}
return () => (effectRan.current = true);
}, [api, ida, reload]);
Here you modify your last edit
const deleteHandler = () => {
// console.log(removeUserId);
setIda(removeUserId);
setReload(!reload)
setShowModal(false);
};
useEffect(() => {
(async () => {
const response = await api.get("/admin/users");
if (response.ok) {
setUserData(response.body);
} else {
setUserData(null);
}
})();
}, [api]);
useEffect(() => {
effectRan.current = reload;
if (effectRan.current) {
// console.log("effect run");
(async () => {
const response = await api.delete("/admin/users", {
ida: ida,
});
if (response.ok && response.status === 204) {
console.log(response);
setReload(!reload);
} else {
console.log(response.body.errors);
}
})();
}
return () => {
effectRan.current = false;
};
}, [api, ida, reload]);
It looks like you're trying to delete an item in the table and confirm it via a prompt. I can see that you've used a lot of useEffects, which might be making things a bit complicated.
Don't worry though, I have a solution that should be much simpler. Let me show you what I mean!
const Page = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState([]);
const [deleting, setDeleting] = useState(false);
const [selectedItem, setSelectedItem] = useState(null);
const deleteHandler = async() => {
setDeleting(true);
await api.delete(selectedItem);
// Next you can either update the state itself
let tD = [...data];
const index = tD.findIndex((i) => i.id == selectedItem);
tD.splice(index, 1);
setSelectedItem(tD);
// or refetch data from the backend and update the state
const d = await api.getData();
setData(d);
setDeleting(false);
setSelectedItem(null);
};
useEffect(() => {
const fetchData = async() => {
setLoading(true);
const d = await api.getData();
setData(d);
setLoading(false);
}
fetchData();
}, [])
return loading ? <div>Loading...</div> : <>
{/*Other JSX elements*/}
{selectedItem && <div>
{/*prompt body*/}
<button onClick={() => {setSelectedItem(null)}}>Cancel</button>
<button disabled={deleting} onClick={() => {deleteHandler()}}>Delete</button>
{/*Button will be disabled when the DELETE request is sent*/}
</div>}
{data.map((row) => {
return <tr key={row.id}>
<td>...</td>
<td>...</td>
<td>
<button onClick={setSelectedItem(row.id)}>Delete</button>
</td>
</tr>
})}
</>
}

How do I get passed this racing condition from Redux Toolkit?

This is not happening in any other slice I created, and it's driving me nuts. I'm out of ideas to try. What I want is simple, pull the payload from my Slice so I can set it to my state and use it! But somehow I am not capturing it. I put a console.log on the Slice for the payload and I am successfully fetching See Pic Below
Now checkout this screenshot with the component on the screen. I noticed that I was getting an error for the 'country' being undefined in pullrank in line 285 which it doesnt mean the path in incorrect or that country doesnt exist, it just breaks because the whole data is empty. If I refresh it all comes up normally, so I commented out the part where I am setting setMinRange and setMaxRange and that's when I realize that allmapdata was setting up empty, Twice nontheless! And then once again with the data in it. Obviously a racing condition, the API is fetching before the data is called, so how do I delay this API call?
Here is my mapSlice
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import axios from "axios";
const KEY = process.env.REACT_APP_API_KEY
const BASE_URL = process.env.REACT_APP_BASE_URL
const GLOBALVIEWS_API = `${BASE_URL}/countrymap`
const initialState = {
mapdata:[],
status: 'idle', //'idle', 'loading', 'succeeded', 'failed'
error:null
}
export const fetchMapData = createAsyncThunk(
'mapdata/fetchMapData',
async (id) => {
try {
console.log("Firing Map reducer");
const response = await axios.get(
GLOBALVIEWS_API,
{
headers: {
'Content-Type': 'application/json',
'X-API-KEY': KEY,
},
params: {
titleId: id,
}
}
)
return response.data;
} catch (error) {
console.error('API call error:', error.message);
}
}
)
const mapSlice = createSlice({
name: 'mapdata',
initialState,
reducers:{
fetchMap(state, action) {},
},
extraReducers(builder) {
builder
.addCase(fetchMapData.pending, (state, action) => {
state.status = 'loading'
})
.addCase(fetchMapData.fulfilled, (state, action) => {
state.status = 'succeeded'
const loadedMapData = action.payload.Item.season_met
state.mapdata = loadedMapData
console.log("loadedMapData: ", loadedMapData);
})
.addCase(fetchMapData.rejected, (state, action) => {
state.status = 'failed'
state.error = action.error.message
})
}
})
// SELECTORS
// allMapData and fetchMap aren't both needed, I'm just trying different ways
export const allMapData = (state) => state.mapdata.mapdata;
export const {fetchMap} = mapSlice.actions;
export const getMapStatus = (state) => state.mapdata.status;
export const getMapError = (state) => state.mapdata.error;
export default mapSlice.reducer
Here's my component (reduced for brevity)
const MetricsCharts = ({selectedIndex}) => {
//STATES
const [minrange, setMinRange] = useState();
const [maxrange, setMaxRange] = useState();
const [map, setMap] = useState([])
const colorScale = scaleLog().domain([minrange, maxrange]).range(["#BAC7FF", "#6128BD"]);
const {id} = useParams();
const dispatch = useDispatch();
const allmapdata = useSelector(allMapData)
const mapdata = useSelector(fetchMap)
const mapStatus = useSelector(getMapStatus)
const error = useSelector(getMapError)
useEffect(() => {
if (mapStatus === 'idle') {
dispatch(fetchMapData(id))
}
setMap(allmapdata)
// const pullrank = allmapdata[selectedIndex].country.map(data => data.lifetime_viewing_subs)
// setMinRange(Math.min(...pullrank))
// setMaxRange(Math.max(...pullrank))
console.log("allmapdata: ", allmapdata);
}, [id, selectedIndex, dispatch, allmapdata, mapStatus, mapdata])
let map_component;
if (mapStatus === 'loading') {
map_component =
<Box className="loading">
<LoadingButton loading
loadingIndicator={
<CircularProgress color="primary" size={50} />
}>
</LoadingButton>
<Typography variant='subtitle2'>Loading content, please wait...</Typography>
</Box>
} else if (mapStatus === 'succeeded') {
map_component =
<>
{map.length > 0 && //checking if something is in the state
<Box sx={{mt:2}}>
<ReactTooltip>{content}</ReactTooltip>
<ComposableMap
width={900}
height={400}
data-tip=""
projectionConfig={{ rotate: [-10, 0, 0], scale:147 }}>
<Sphere stroke="#000" strokeWidth={0.3} />
<Graticule stroke="#000" strokeWidth={0.3} />
<Geographies geography={geoUrl}>
{({ geographies }) => geographies.map((geo, index) => {
const countries = map[selectedIndex].country.find( (s) => s.ISO3 === geo.properties.ISO_A3);
return (
<Fragment key={index}>
<Geography
key={geo.rsmKey}
geography={geo}
onMouseEnter={() => {
setContent(
countries
? `${geo.properties.NAME_LONG} — ${rounded(Math.round(countries.lifetime_viewing_subs))}`
: ""
);
}}
fill={
countries
? colorScale(countries["lifetime_viewing_subs"])
: "#333"
}
/>
</Fragment>
);
})
}
</Geographies>
</ComposableMap>
</Box>
}
</>
} else if (mapStatus === 'failed') {
map_component =
<Box className="loading">
<Typography variant="subtitle2">{error}</Typography>
</Box>
}
return (
<div>
{map_component}
</div>
)
}
export default MetricsCharts
As you can see, I can't just give you a CODEPEN cause the map dependency is incredibly large and complex. I'm sorry about that. I do appreciate any help

Component re-renders self after API call whilst using useRef

I'm working on a project and wanted to try and implement an infinitely scrollable page to list users. Filtering and such works fine and all but every time the scrolling component reaches the ref element the component makes an API call to append to the list and then the parent component re-renders self completely.
const UsersList = () => {
const [searchString, setSearchString] = useState('')
const [next, setNext] = useState('')
const { userList, error, nextPage, loading, hasMore } = useFetch(next)
const [usersList, setUsersList] = useState([])
const observer = useRef()
const lastElemRef = useCallback(
(node) => {
if (loading) return
if (observer.current) observer.current.disconnect()
observer.current = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && hasMore) {
setNext((prev) => (prev = nextPage))
setUsersList((prev) => new Set([...prev, ...userList]))
}
})
if (node) {
observer.current.observe(node)
}
},
[loading, nextPage, hasMore],
)
useEffect(() => {
setUsersList((prev) => new Set([...prev, ...userList]))
console.log(error)
}, [])
return (
<>
{loading ? (
<CSpinner variant="grow"></CSpinner>
) : (
<CContainer
className="w-100 justify-content-center"
style={{ maxWidth: 'inherit', overflowY: 'auto', height: 600 }}
>
<CFormInput
className="mt-2 "
id="userSearchInput"
value={searchString}
onChange={(e) => {
setSearchString(e.target.value)
}}
/>
{loading ? (
<CSpinner variant="grow"></CSpinner>
) : (
<>
{Array.from(usersList)
.filter((f) => f.username.includes(searchString) || searchString === '')
.map((user) => {
if (Array.from(usersList)[usersList.size - 1] === user) {
return (
<UserCard
key={user.id}
user={user}
parentRef={searchString ? null : lastElemRef}
/>
)
} else {
return <UserCard key={user.id} user={user} />
}
})}
</>
)}
</CContainer>
)}
</>
)
}
export default UsersList
This is the component entirely.
Here's my useFetch hook;
export const useFetch = (next) => {
const [userList, setUserList] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
const [nextPage, setNextPage] = useState(next)
const [hasMore, setHasMore] = useState(true)
useEffect(() => {
setLoading(true)
setError('')
axios
.get(next !== '' ? `${next}` : 'http://localhost:8000/api/getUsers/', {
headers: {
Authorization: 'Bearer ' + localStorage.getItem('access_token'),
},
})
.then((res) => {
setUserList((userList) => new Set([...userList, ...res.data.results]))
setNextPage((prev) => (prev = res.data.next))
if (res.data.next === null) setHasMore(false)
setLoading(false)
})
.catch((err) => {
setError(err)
})
}, [next])
return { userList, error, nextPage, loading, hasMore }
}
export default useFetch
I'm using Limit Offset Pagination provided by Django Rest Framework, next object just points to the next set of objects to fetch parameters include ?limit and ?offset added at the end of base API url. What is it that I'm doing wrong here ? I've tried many different solutions and nothing seems to work.
Solved
Apparently it was just my back-end not cooperating with my front-end so I've changed up the pagination type and now it seems to behave it self.

Redux State Management with a Input field

Bit of a noob to redux but this appears to be quite a difficult question! I hope someone may be able to help me :)
I have build a page where you can input a search for different types of jobs. From this, it will make a get request to my DB and get all the info on this job. As this page is multi-levelled, I want to use redux to dispatch and pass the state throughout. This will help me pass my data on the job, e.g Data Analyst, through to each component so it can use the data and populate fields.
However, this was how my input field was originally setup:
export function SearchBarComp(props) {
const [isExpanded, setExpanded] = useState(false);
const [parentRef, isClickedOutside ] = useClickOutside();
const inputRef = useRef();
const [searchQuery, setSearchQuery] = useState("");
const [isLoading, setLoading] = useState(false);
const [jobPostings, setjobPostings] = useState([]);
const [noRoles, setNoRoles] = useState(false)
const isEmpty = !jobPostings || jobPostings.length === 0;
const changeHandler = (e) => {
//prevents defaulting, autocomplete
e.preventDefault();
if(e.target.value.trim() === '') setNoRoles(false);
setSearchQuery(e.target.value);
}
const expandedContainer = () => {
setExpanded(true);
}
//LINK THE BACKEND!
const prepareSearchQuery = (query) => {
//const url = `http://localhost:5000/api/role/title?title=${query}`;
const url = `http://localhost:5000/api/role/titlerole?title=${query}`;
//replaces bad query in the url
return encodeURI(url);
}
const searchRolePosition = async () => {
if(!searchQuery || searchQuery.trim() === "")
return;
setLoading(true);
setNoRoles(false);
const URL = prepareSearchQuery(searchQuery);
const response = await axios.get(URL).catch((err) => {
console.log(err);
});
if(response) {
console.log("Response", response.data);
if(response.data && response.data === 0)
setNoRoles(true);
setjobPostings(response.data);
}
setLoading(false);
}
useDebounce(searchQuery, 500, searchRolePosition)
const collapseContainer = () => {
setExpanded(false);
setSearchQuery("");
setLoading(false);
setNoRoles(false);
if (inputRef.current) inputRef.current.value = "";
};
// console.log("Value", searchQuery)
useEffect(()=> {
if(isClickedOutside)
collapseContainer();
}, [isClickedOutside])
return (
<SearchBarContainer animate = {isExpanded ? "expanded" : "collapsed"}
variants={containerVariants} transition={containerTransition} ref={parentRef}>
<SearchInputContainer>
<SearchIconSpan>
<SearchIcon/>
</SearchIconSpan>
<SearchInput placeholder = "Search for Roles"
onFocus={expandedContainer}
ref={inputRef}
value={searchQuery}
onChange={changeHandler}
/>
<AnimatePresence>
{isExpanded && (<CloseIconSpan key="close-icon"
inital={{opacity:0, rotate: 0}}
animate={{opacity:1, rotate: 180}}
exit={{opacity:0, rotate: 0}}
transition={{duration: 0.2}}
onClick={collapseContainer}>
<CloseIcon/>
</CloseIconSpan>
)}
</AnimatePresence>
</SearchInputContainer>
{isExpanded && <LineSeperator/>}
{isExpanded && <SearchContent>
{!isLoading && isEmpty && !noRoles && (
<Typography color="gray" display="flex" flex="0.2" alignSelf="center" justifySelf="center">
Start typing to search
</Typography>
)}
{!isLoading && !isEmpty && <>
{jobPostings.map((searchRolePosition) => (
<JobSection
title={searchRolePosition.title}
//will need to do something like ----
//people = {searchRolePosition.title && searchRolePosition.title.average}
// future implementations
/>
))}
</>}
</SearchContent>}
</SearchBarContainer>
)
}
As you can see, the main thing is the 'query' this creates a backend request to my titlerole, such as getting the data on Data Analyst. This all works in my frontend right now, but I can't pass that data down to the next component etc
So i'm looking to use redux.
I've created the following slice:
import { createSlice } from "#reduxjs/toolkit";
const jobSearchSlice = createSlice({
name: "jobsearch",
initialState: {
currentRole: null,
isFetching: false,
error: false,
},
reducers: {
jobsearchStart: (state) => {
state.isFetching = true;
},
jobsearchSuccess: (state, action) => {
state.isFetching = false;
state.currentRole = action.payload;
},
jobsearchFailure: (state) => {
state.isFetching = false;
state.error = true;
},
},
});
export const { jobsearchStart, jobsearchSuccess, jobsearchFailure } = jobSearchSlice.actions;
export default jobSearchSlice.reducer;
With this, I'm also using the following apiCalls.js file:
import { jobsearchStart, jobsearchSuccess, jobsearchFailure } from "./jobsearchSlice";
import { publicRequest } from "../requestMethods";
export const roleSearchQuery = async (dispatch, jobsearch) => {
dispatch(jobsearchStart());
try{
const res = await publicRequest.get("`http://localhost:5000/api/role/titlerole?title=${query}`", jobsearch);
dispatch(jobsearchSuccess(res.data))
}catch(err){
dispatch(jobsearchFailure());
}
};
My question is as a Redux noob, how do I implement this query functionality into a redux API request? What's the way to do this properly as I begin to tranisition this to an app which uses standard state management!

How to pass value through props to another component and error : `Use the defaultValue or value props on instead of setting selected on <option>`?

I am trying to pass the selected value through the prop: handle state change, but I am getting two errors.
Error 1 : Use the defaultValue or value props on instead of setting selected on <option>
Error 2 : cannot read property 'active' of undefined at StateData
import React,{useState,useEffect} from 'react';
import { FormControl , makeStyles, InputLabel,Select} from '#material-ui/core';
import {options} from '../../api/api'
const useStyles = makeStyles((theme) => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}));
const ComboBox = ({handleStateChange}) =>{
const styles = useStyles();
const [fetchedStates,setFetchedStates] = useState([]);
const [ value , setValue] = useState('')
useEffect(()=>{
const fetchAPI = async() => {
setFetchedStates(await options());
}
fetchAPI()
},[setFetchedStates]);
const inputEvent = (e) =>{
console.log(e.target.value)
setValue(e.target.value)
handleStateChange(e.target.value)
}
return (
<div>
<FormControl variant="outlined" className={styles.formControl}>
<InputLabel>States</InputLabel>
<Select
defaultValue=''
value={value}
onChange={inputEvent}
label="States"
>
{fetchedStates.map((states, i) => <option key={i} value={states}>{states}</option>)}
</Select>
</FormControl>
</div>
);
}
export default ComboBox
//***This is where the API call happens***//
var url = 'https://api.covid19india.org/data.json';
export const StateData = async(states) =>{
let stateName = 'Nagaland'
if(states){
stateName = {states}
}
const response =await fetch(url);
const data = await response.json();
const pop = data.statewise;
const index = pop.findIndex(st => st.state === stateName)
const statedta = {
active :pop[index].active,
confirmed : pop[index].confirmed,
deaths : pop[index].deaths,
recovered: pop[index].recovered
}
return statedta
}
export async function ChartData() {
try{
let response = await fetch(`https://api.covid19india.org/data.json`);
return await response.json()
.then(data=>{
const pop = data.cases_time_series
const modifiedData = pop.map((totalData)=>({
confirmed : totalData.totalconfirmed,
deaths : totalData.totaldeceased,
recovered : totalData.totalrecovered,
date : totalData.date
}))
return modifiedData;
});
}catch(err){
console.error(err);
}
}
export async function options() {
const response =await fetch(url);
const data = await response.json();
const pop = data.statewise.map(st=>st.state);
return pop
}
You need to make few fixes.
Use NativeSelect if you are using your own options.
There was a bug in StateData fun. Don't put states in an object.
use a separate useEffect to call StateDatafun.
All your issues are fixed. See working demo of your code
jsx snippet
export default function ComboBox({ handleStateChange }) {
const [fetchedStates, setFetchedStates] = useState([]);
const [value, setValue] = useState("");
const [individualStateDetails, setIndividualStateDetails] = useState("");
useEffect(() => {
const fetchAPI = async () => {
setFetchedStates(await options());
};
fetchAPI();
}, [setFetchedStates]);
useEffect(() => {
if (fetchedStates.length) {
StateData(value).then(data => {
setIndividualStateDetails(data);
});
}
}, [value]);
const inputEvent = e => {
console.log(e.target.value);
setValue(e.target.value);
handleStateChange(e.target.value);
};
const [age, setAge] = React.useState("");
const handleChange = event => {
setAge(event.target.value);
};
return (
<div>
<FormControl variant="outlined">
<InputLabel>States</InputLabel>
<NativeSelect
defaultValue=""
// value={value}
onChange={inputEvent}
label="States"
>
{fetchedStates.map((states, i) => (
<option key={states + i} value={states}>
{states}
</option>
))}
</NativeSelect>
</FormControl>
<div>
{individualStateDetails &&
JSON.stringify(individualStateDetails, null, 2)}
</div>
</div>
);
}

Categories