changing value of dropdown using react hook - javascript

I have one edit form. its values are getting auto populated once user click on the edit button. actually all the values are getting populated properly except the dropdown. its still same. how can I change the valye of dropdown using react hook?
below method populating data in the dropdown.
const fetchStatus = async ()=>{
const {data}= await httpClient.get( config.resourceServerUrl+"/sponsors");
data.length>0? setResult(data): setResult([]);
}
below useEffect setting the values in the field once page loaded. before that it calls above method to populate values in dropdown because it comes from database.
useEffect(() => {
fetchStatus();
(async () => {
const { data } = await httpClient.get(config.resourceServerUrl+"/certificates/"+id);
setRequestStatus(data.requestStatus);
setSponsor(data.sponsor);
setAddress(data.address);
setPhaseOfTrial(data.phaseOfTrial);
setAddress2(data.address2);
setZipCOde(data.zipCode);
setCity(data.city);
setprotoColNo(data.protoColNo);
});
below are the react hooks I created.
const [requestStatus, setRequestStatus] = useState();
const [sponsor, setSponsor] = useState();
const [address, setAddress] = useState();
const [phaseOfTrial, setPhaseOfTrial] = useState();
const [address2, setAddress2] = useState();
const [zipCode, setZipCOde] = useState();
const [city, setCity] = useState();
const [protoColNo, setprotoColNo] = useState();
below hook is getting used for selecting the value in the dropdown..
const [sponsor, setSponsor] = useState();
what changes I need to do to select the value in dropdown once page loaded. later user can change.
I am usin react-select and formik in the page. when user will change the value in dropdown other field will also populate.
<div className="row mt-0 ">
<div className="col-sm-6 pb-2 gx-5">
<label
id="sponsorLabel"
className="required form-label"
>
Sponsor
</label>
<Select
id="sponsor"
name="sponsor"
// placeholder="Select the Sponsor"
className="align-items-center justify-content-center"
options={result.map((sponsor:Sponsor)=>
({ label: sponsor.name, value: sponsor.id })
)}
onChange={ (sponsorId:any,e:any)=>{
setFieldValue("sponsor",sponsorId.value)
result.map((sponsor:Sponsor)=>{
if(sponsorId.value===sponsor.id){
setFieldValue("address",sponsor.address);
setFieldValue("address2",sponsor.address2);
setFieldValue("zipCode",sponsor.zipCode);
setFieldValue("city",sponsor.city);
setFieldValue("sponsor",sponsor.name)
}
})
}}
/>
<span className={styles.mandatory}>
<ErrorMessage name="sponsor" />
</span>
</div>
</div>
below is the data to populate list.
const fetchStatus = async ()=>{
const {data}= await httpClient.get( config.resourceServerUrl+"/sponsors");
data.length>0? setResult(data): setResult([]);
}

Related

I'm having trouble updating state with a select menu

As the title suggests, I'm having issues updating state with a select menu. I'm not sure if the trouble is coming from the fact I'm trying to update it from multiple sources?
getSurvivorPerks fetches an array of objects. On page load a random 4 are selected to be displayed, and these four are randomized on each handlesubmit. I would like to be able to manually select the individual perks for perk1, 2, etc with a select menu. As of now, this just results in perk1 getting set to Null. The data does display appropriately in the select menu.
export default function SurvivorRandomizer() {
const [survivorPerk1, setSurvivorPerk1] = useState({});
const [survivorPerk2, setSurvivorPerk2] = useState({});
const [survivorPerk3, setSurvivorPerk3] = useState({});
const [survivorPerk4, setSurvivorPerk4] = useState({});
const [perkList, setPerkList] = useState([]);
const [loading, setLoading] = useState(true);
const { user } = useUser();
useEffect(() => {
const fetchData = async () => {
const data = await getSurvivorPerks();
let perks = randomPerks(data);
setPerkList(data);
setSurvivorPerk1(perks[0]);
setSurvivorPerk2(perks[1]);
setSurvivorPerk3(perks[2]);
setSurvivorPerk4(perks[3]);
setLoading(false);
};
fetchData();
}, []);
const handleSubmit = () => {
let perks = randomPerks(perkList);
setSurvivorPerk1(perks[0]);
setSurvivorPerk2(perks[1]);
setSurvivorPerk3(perks[2]);
setSurvivorPerk4(perks[3]);
};
if (loading) return <h1>loading...</h1>;
return (
<>
<div className="perk-row-1">
<div className="perk-card">
<PerkCard {...survivorPerk1} />
<select value={perkList.perk} onChange={(e) => setSurvivorPerk1(e.target.value)}>
<option>Select...</option>
{perkList.map((perk) => (
<option key={uuid()} value={perk}>
{perk.name}
</option>
))}
</select>
</div>
The issue here (had my blinders on) is that the value cannot be an object.
const handlePerkSelect = async (perk, id) => {
let setPerk = await getSurvivorPerkById(id);
perk(setPerk);
};
-------------
<select onChange={(e) => handlePerkSelect(setSurvivorPerk1, e.target.value)}>
<option>Select...</option>
{perkList.map((perk) => (
<option key={uuid()} value={perk.ID}>
{perk.name}
</option>
))}
</select>
This was my solution. There's definitely a better way to do it that doesn't involve making more fetch requests, and I'll likely refactor, but on the off chance this helps someone, I figured I'd share what the issue was.

React not setting state from fetch data

I have a piece of react JS code that is supposed to fetch data from an endpoint and populate a form from the data.
The main issue I'm having with is that it only populates the first field.
It does not populate the rest.
The react component is as below
import React, { useState, useCallback, useEffect } from "react";
import { Page, Button, Stack, Card, Form, FormLayout, TextField, TextContainer, Modal, Toast, TextStyle, Loading } from "#shopify/polaris";
import axiosInstance from "../common/RequestHandler";
import { useParams } from 'react-router-dom';
import { useNavigate } from "react-router";
function EditPackages(){
const [errorToastActive, setErrorToastActive] = useState(false);
const [active, setActive] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [height, setHeight] = useState('');
const [width, setWidth] = useState('');
const [depth, setDepth] = useState('');
const [maxWeight, setMaxWeight] = useState('');
const [packageName, setPackageName] = useState('');
const [packageId, setPackageId] = useState(null);
const [btnLoadingState, setBtnLoadingState] = useState(false);
const [btnLoadingState, setBtnLoadingState] = useState(false);
const toggleErrorToastActive = useCallback(() => setErrorToastActive((errorToastActive) => !errorToastActive), []);
const errorToastMarkUp = errorToastActive ? (
<Toast content="Error in editing your package" error onDismiss={toggleErrorToastActive} />
) : null;
const params = useParams();
const editPackageId = params.editPackageId;
console.log("Edit Package ID -> ", editPackageId);
const navigate = useNavigate();
useEffect(async () => {
const data = await retrievePackage();
console.log(data);
setMaxWeight(data.maxWeight);
setDepth(data.depth);
setHeight(data.height);
setWidth(data.width);
setPackageName(data.packageName);
}, [editPackageId]);
const backToPackages = function (){
navigate('/app/packages');
}
const getPackage = useCallback(async () => {
setPackageInfo(await retrievePackage());
}, []);
async function retrievePackage(){
setIsLoading(true);
const resp1 = await axiosInstance.get('/packageInfo?packageId=' + editPackageId);
setIsLoading(false);
return await resp1.data;
}
return (
<Page title="Edit Package" fullWidth>
{errorToastMarkUp}
<Form>
<FormLayout>
<TextField label="Package Name" value={packageName} onChange={setPackageName} autoComplete="off" />
<TextField label="Height in CM" value={height} onChange={setHeight} autoComplete="off" />
<TextField label="Width in CM" value={width} onChange={setWidth} autoComplete="off" />
<TextField label="Depth in CM" value={depth} onChange={setDepth} autoComplete="off" />
<TextField label="Max Weight in Grams" value={maxWeight} onChange={setMaxWeight} autoComplete="off" />
<Button submit>Submit</Button>
</FormLayout>
</Form>
</Page>
);
}
export default EditPackages;
The getPackage method is to retrieve the data from the endpoint and I'm expecting the setPackageInfo to set the state/values for the object.
I can confirm the API data is retrieved and to confuse me even more, it populates the textbox with packageInfo.packageName. But the rest, none.
I'm sure the names match with the data retrieve as well.
For better understanding, below is my response from the endpoint.
{
"id": 25,
"mId": 1,
"height": 123,
"width": 35,
"depth": 3,
"maxWeight": 4566,
"created_at": "2022-02-18T21:13:47.000000Z",
"updated_at": "2022-02-18T21:13:47.000000Z",
"packageName": "Some random name"
}
Any help is greatly appreciate. I've been hitting my head on a brick wall for days with this problem. Thank you in advance.
It seems like the form has some internal state.
Shopify has a package for managing form state: #shopify/react-form-state.
For this example, if you want to keep it simple, make a state hook for every field. This is not slow since react groups setState calls in rerenders and if multiple requests are made, the page wont refresh depending on the field count but only once.
const [packageName, setPackageName] = useState("");
const [height, setHeight] = useState("");
async function retrievePackage(){
setIsLoading(true);
const response = await axiosInstance.get('/packageInfo?packageId=' + editPackageId);
setIsLoading(false);
return response.data;
}
useEffect(() => {
const data = await retrievePackage();
setPackageName(data.packageName);
setHeight(data.height);
...
}, []);
<TextField
value={packageName}
onChange={setPackageName}
/>
<TextField
value={height}
onChange={setHeight}
/>
Then submit the object composed from all the hooks
const formData = {
packageName,
height,
}
...
Your onChange method is incorrect, that's why you get an object in the response from axios, but once one of the onChange(s) runs, the state changes and you are left with the first field in the tree only and the other values become null/undefined.
Try changing the onChange method to - e=> setPackageInfo({...packageInfo, packageName: e.target.value}) for packageName, e=> setPackageInfo({...packageInfo, height: e.target.value}) for height, and so on.

How can I disable/enable the button based on multiple checkboxes state in React.js

I have a component on a page that renders a bunch of checkboxes and toggles.
I also have a button called confirm at the bottom to save the changes and make a request to update the back-end.
However I wanted to support a feature that when users haven't made any changes to any of these checkboxes or toggles, the confirm button should be disabled. and when users toggle or check any of these, then the confirm button is enabled and clickable.
so right now what I am doing is
const MyComponent = () => {
const dispatch = useDispatch();
// get the current state from the store
const state = useSelector((state: RootState) => state.settings);
const [isCheckbox1Checked, setCheckbox1] = useState(false);
const [isCheckbox2Checked, setCheckbox2] = useState(false);
const [isCheckbox3Checked, setCheckbox3] = useState(false);
const [isConfirmBtnEnabled, setConfirmBtn] = useState(false);
const [updateBtnEnabled, enableUpdateBtn] = useState(false);
useEffect(() => {
(async () => {
// `getSettingsConfig` is a async thunk action
await dispatch(getSettingsConfig());
setCheckbox1(state.isCheckbox1Checked);
setCheckbox2(state.isCheckbox2Checked);
setCheckbox3(state.isCheckbox3Checked);
})();
}, [
dispatch,
state.isCheckbox1Checked,
state.isCheckbox2Checked,
state.isCheckbox3Checked
// ..
]);
return (
<>
<div className="checkboxes">
<Checkbox1
onCheck={() => {
setCheckbox1(true);
setConfirmBtn(true);
}}
/>
<Checkbox2
onCheck={() => {
setCheckbox2(true);
setConfirmBtn(true);
}}
/>
<Checkbox3
onCheck={() => {
setCheckbox3(true);
setConfirmBtn(true);
}}
/>
</div>
<button disabled={!isConfirmBtnEnabled}>Confirm</button>
</>
);
};
right now it seems to be working out fine but it requires manually spamming setConfirmBtn to every checkbox and toggles I have on this page. I wonder if there is a better way to do it.
Also I thought about using useEffect to call isConfirmBtnEnabled every time any of these state changes. However since the initial state is derived from the store via dispatching an async thunk, the state of these checkboxes and toggles are going to be changed anyways after the page mounts, so that means I cannot use another useEffect to listen on the changes of these state.
You could use useEffect hook to watch the three check boxes and update the button state based on isConfirmBtnEnabled which is updated inside the useEffect hook:
useEffect(()=>{
setConfirmBtn(isCheckbox1Checked || isCheckbox2Checked || isCheckbox3Checked)
},[isCheckbox1Checked,isCheckbox2Checked,isCheckbox3Checked])
Edit :
const MyComponent = () => {
const dispatch = useDispatch();
// get the current state from the store
const state = useSelector((state: RootState) => state.settings);
const [checkboxes, setCheckboxes] = useState({c1:false,c2:false,c3:false});
const [isConfirmBtnEnabled, setConfirmBtn] = useState(false);
const [updateBtnEnabled, enableUpdateBtn] = useState(false);
useEffect(() => {
(async () => {
// `getSettingsConfig` is a async thunk action
await dispatch(getSettingsConfig());
[1,2,3].forEach(i=>{
setCheckboxes({...checkboxes,[`c${i}`]:state[`isCheckbox${1}Checked`]})
})
})();
}, [
dispatch,
state
// ..
]);
useEffect(()=>{
setConfirmBtn(Object.values(checkboxes).reduce((a,c)=>(a=a || c),false))
},[checkboxes])
const _onCheck=(i)=>{
setCheckboxes({...checkboxes,[`c${i}`]:tur})
}
return (
<>
<div className="checkboxes">
<Checkbox1
onCheck={() => _onCheck(1)}
/>
<Checkbox2
onCheck={() => _onCheck(2)}
/>
<Checkbox3
onCheck={() => _onCheck(3)}
/>
</div>
<button disabled={!isConfirmBtnEnabled}>Confirm</button>
</>
);
};

How do I pass data from one sibling to another using React Router?

I have a search bar with a submit button that fetches data from an API, all located in a fixed Nav bar. Every time I click submit, I would like React Router to change to a results page, and display the results.
I can't seem to figure out how to pass the data, either as props or as state to this new component. Here is my search API code:
const Search = () => {
const apiKey = 'xxxxxx';
const [input, setInput] = useState('');
const [items, setItems] = useState([]);
const handleSubmit = (e) => {
searchAPI();
};
const searchAPI = async () => {
const res = await fetch(`http://www.omdbapi.com/?apikey=${apiKey}&s=${input}`);
const data = await res.json();
setItems(data.Search);
};
return (
<form>
<input onChange={(e) => setInput(e.target.value)}></input>
<Link to={{ pathname: '/results', state: items }}>
<button type="submit" onClick={handleSubmit}>
search
</button>
</Link>
</form>
);
};
And here is the code in my results component:
const SearchResults = () => {
const [results, setResults] = useState([]);
return (
<div>
<h1>RESULTS</h1>
{results.map((result) => {
return <li key={results.Search.imdbID}>{result.Search.Title}</li>;
})}
</div>
);
};
How would I go about getting the data from items in my search API component into results in my results component?
You are so close. You could use useLocation hook from "react-router-dom" in your SearchResults component like so
const { state } = useLocation();
Items that you pass from the Link would be there.
Use:
<Link to="results" myData={{ state: items }}>
In results components you can access params:
import {useRouteMatch} from 'react-router-dom'
const {params} = useRouteMatch();
console.log(params.myData);

How to select DOM elements from map function in react component?

In this simple components I want to select all inputs and listen to a change event, but I got just a null node list, but me I want the selected inputs.
How I can select inputs inside this map function ?
const Component = () => {
const [categories, setCategories] = useState([]);
const [loading, setLoading] = useState(false);
const myFun = () => {
// I want to get all inputs of type cheeckbox;
const inps = document.querySelectorAll("input[type=checkbox]");
console.log(inps); // but I get nodeList[]
}
useEffect(() => {
setCategories([...Fetched Categories from the server])
setLoading(true);
myFunc();
}, []);
return <div id="component">
{ loading && (categories.map((c,i)=>(
<div className="form-group" key={i}>
<label htmlFor={c.id}>
<input type="checkbox" value={c.id} id={c.id} />
{c.title}
</label>
</div>
)))
}
</div>
}
export default Component;
There is no inputs when you call myFunc(). Your setLoading(true) will affect html only on next render.
The best way to access dom nodes in react is to use thise hook https://reactjs.org/docs/hooks-reference.html#useref

Categories

Resources