I have user with two parameters (username, and user groups )
I have Page that's update my user by changing username and update groups
it looks like:
Problem is , I can't highlight groups , I need to choose to update .
const AddUser = props =>{
let editing = false;
let initialUsername = "";
const[initialGroups, setInitialGroups] = useState([])
useEffect(()=>{
retrieveGroups();
},[])
const retrieveGroups = () => {
BackendService.getAllGroups()
.then(response => {
setInitialGroups(response.data);
})
.catch(e => {
console.log(e);
});
}
const[username, setUsername] = useState(initialUsername);
const[groups, setGroups] = useState(initialGroups);
const[submitted, setSubmitted] = useState(false);
const onChangeUsername = e => {
const username = e.target.value;
setUsername(username);
}
const onChangeGroups = e => {
console.log(e);
setGroups(Array.from(e.currentTarget.selectedOptions, (v) => v.value));
}
const saveUser = () => {
var data = {
username: username,
groups: groups,
complited: false,
}
BackendService.editUser(
props.location.state.currentUser.id,
data)
.then(response=>{
setSubmitted(true);
console.log(response.data)
})
.catch(e=>{
console.log(e);
})
)
.catch(e=>{
console.log(e);
});
}
}
return(
<Container>
{submitted ? (
<div>
<h4>
User Edited Successfully
</h4>
<Link to={"/users/"}></Link>
Back to Users
</div>
):(
<Form>
<Form.Group className="mb-3">
<Form.Label>
"Edit" User
</Form.Label>
<Form.Control
type="text"
required
value={username}
placeholder="Enter username here"
onChange={onChangeUsername}
/>
<Form.Control
as="select"
multiple value={initialGroups}
onChange={onChangeGroups}
>
{initialGroups.map(group => (
<option key={group.id} value={group.id}>
{group.name}
</option>
))}
</Form.Control>
</Form.Group>
<Button variant="info" onClick={saveUser}>
"Edit" User
</Button>
</Form>
)}
</Container>
)
}
export default AddUser;
In this section I get all groups(initialGroups) I have in database:
const[initialGroups, setInitialGroups] = useState([])
useEffect(()=>{
retrieveGroups();
},[])
const retrieveGroups = () => {
BackendService.getAllGroups()
.then(response => {
setInitialGroups(response.data);
})
.catch(e => {
console.log(e);
});
}
After I put InitialGroups in :
<Form.Control
as="select"
multiple value={initialGroups}
onChange={onChangeGroups}
>
{initialGroups.map(group => (
<option key={group.id} value={group.id}>
{group.name}
</option>
))}
</Form.Control>
And process in :
const onChangeGroups = e => {
console.log(e);
setGroups(Array.from(e.currentTarget.selectedOptions, (v) => v.value));
}
What I do wrong ? I can't highlight group I need to proces and update user
I would like to make the following suggestions:
You don't need to use 'initialGroups' as a state variable. You already have a 'groups' variable which can receive an initial value.
const [groups, setGroups] = useState([]);
You can directly set the 'groups' variable once you retrieve the data.
BackendService.getAllGroups()
.then(response => {
setGroups(response.data);
})
You should pass 'groups' to the 'value' prop on the Form component, instead of 'initialGroups' (and then map over it).
<Form.Control
as="select"
multiple
value={groups}
onChange={onChangeGroups}
>
{groups.map(group => (
<option key={group.id} value={group.id}>
{group.name}
</option>
))}
</Form.Control>
Hope this helps!
Related
Hello everyone and thank you for reading this! Here is my problem that i can't solve:
My application has the following functionality:
There are 2 inputs, then a button, when clicked, 2 more inputs appear and a button to send data from all inputs to the console, however, in the additional field, one input is required. This is where my problem arises: now, if I called additional inputs and filled in all the data, they are transferred to the console, if I didn’t fill in the required field, an error message goes to the console, BUT. I also need, in the event that I did NOT call additional inputs, the data of 2 basic inputs was transferred to the console. At the moment I can't figure it out.
import React, { useState } from "react";
import ReactDOM from "react-dom/client";
import produce from "immer";
const FunctionalBlock = ({
id,
idx,
isDeleted,
toggleBlockState,
additionalValue,
additionalTitle,
setNewBlock,
index,
}) => {
return (
<div
style={{
display: "flex",
maxWidth: "300px",
justifyContent: "space-between",
}}
>
{!isDeleted ? (
<React.Fragment>
<strong>{idx}</strong>
<input
type="text"
value={additionalTitle}
onChange={(e) => {
const additionalTitle = e.target.value;
setNewBlock((currentForm) =>
produce(currentForm, (v) => {
v[index].additionalTitle = additionalTitle;
})
);
}}
/>
<input
type="text"
value={additionalValue}
onChange={(e) => {
const additionalValue = e.target.value;
setNewBlock((currentForm) =>
produce(currentForm, (v) => {
v[index].additionalValue = additionalValue;
})
);
}}
/>
<button onClick={toggleBlockState}>now delete me</button>
</React.Fragment>
) : (
<button onClick={toggleBlockState}>REVIVE BLOCK</button>
)}
</div>
);
};
const Application = () => {
const [newBlock, setNewBlock] = useState([]);
const [firstInput, setFirstInput] = useState("");
const [secondInput, setSecondInput] = useState("");
const getNewBlock = (idx) => ({
id: Date.now(),
idx,
isDeleted: false,
additionalValue: "",
additionalTitle: "",
});
const toggleIsDeletedById = (id, block) => {
if (id !== block.id) return block;
return {
...block,
isDeleted: !block.isDeleted,
};
};
const createOnClick = () => {
const block = getNewBlock(newBlock.length + 1);
setNewBlock([...newBlock, block]);
};
const toggleBlockStateById = (id) => {
setNewBlock(newBlock.map((block) => toggleIsDeletedById(id, block)));
};
const showInputData = () => {
newBlock.map((item) => {
if (item.additionalTitle.length < 3) {
console.log("it is less than 3");
} else if (!item.additionalTitle && !item.additionalValue) {
console.log(firstInput, secondInput);
} else {
console.log(
firstInput,
secondInput,
item.additionalTitle,
item.additionalValue
);
}
});
};
return (
<div>
<div>
<input
type="text"
value={firstInput}
onChange={(e) => {
setFirstInput(e.target.value);
}}
/>
<input
type="text"
value={secondInput}
onChange={(e) => {
setSecondInput(e.target.value);
}}
/>
</div>
<div>
<button onClick={createOnClick}>ADD NEW INPUTS</button>
</div>
<div>
{newBlock.map((block, index) => (
<FunctionalBlock
key={index}
{...block}
toggleBlockState={() => toggleBlockStateById(block.id)}
setNewBlock={setNewBlock}
index={index}
/>
))}
</div>
<button onClick={showInputData}>send data</button>
</div>
);
};
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Application />);
Here is this code on sandbox for those who decided to help me. Thank you!
https://codesandbox.io/s/vigilant-booth-xnef6t
I am working on country selector component I want to update the state value only when user click on submit button.
I tried to store selected drop down value in temp variable But the select drop down value will not reflect in UI again get the old value in UI
HeaderLeftComponent (Here i am using drop down state)
const HeaderLeftSection = ( props ) => {
const [showCountry, setShowCountry] = useState(false);
const [ShowAccount, setShowAccount] = useState(false);
const [ShowWishlist, setShowWishlist] = useState(false);
const [countrySelector, setCountrySelector] = useState({
country: "IN",
currency: "INR"
});
const handleShowCountry = () => {
setShowCountry(true);
}
const handleHideCountry = () => {
setShowCountry(false);
}
const handleShowAccount = () => {
setShowAccount(true);
}
const handleHideAccount = () => {
setShowAccount(false);
}
const handleShowWishlist = () => {
setShowWishlist(true);
}
const handleHideWishlist = () => {
setShowWishlist(false);
}
const handleCountryAndCurrencyChange = (item) => {
const Country = item;
}
const handleSaveCountry = (item) => {
alert();
setShowCountry(false);
}
return (
<>
<Model
show={showCountry}
hide={ handleHideCountry }
classModal="Country__selector--modal"
handleButtonClicked={handleSaveCountry}
ModelHeader="true"
ModelFooter="true"
ButtonOneText="Save & Continue"
ButtonSecondText="Cancel"
block="true">
<CountrySelector
countrySelector={countrySelector}
handleCountryAndCurrencyChange={handleCountryAndCurrencyChange}
/>
</Model>
<Model
show={ShowAccount}
hide={handleHideAccount}
classModal="Account__form--modal"
ModelHeader="true"
ModelHeaderText="SIGN IN">
<Login />
</Model>
<Model show={ShowWishlist} hide={handleHideWishlist} ModelHeader="true" ModelHeaderText="WISHLIST" classModal="Wishlist__form--modal">
<p>WishList Test</p>
</Model>
<div className="Header__left-section">
<div className="Header__left--countrySelector mr-5 icon" onClick={handleShowCountry}>
<span>{countrySelector.country}</span>
<span>{countrySelector.currency}</span>
</div>
<div className="Header__left--account mr-5">
<Icon.Person className="icon" onClick={handleShowAccount}/>
</div>
<div className="Header__left--wishlist">
<Icon.Heart className="icon" onClick={handleShowWishlist}/>
</div>
</div>
</>
)
}
export default HeaderLeftSection;
Country Selector Component
const CountrySelector = ( props ) => {
return (
<>
<p>Shipping somewhere else?</p>
<p>Simply select the destination you would like to deliver to and the currency you would like to shop in.</p>
<Select List={countryList} label="Delivery To" size="sm" value={props.countrySelector.country} handleCountryAndCurrencyChange={props.handleCountryAndCurrencyChange}/>
<Select List={currencyList} label="Shop in" size="sm" value={props.countrySelector.currency} handleCountryAndCurrencyChange={props.handleCountryAndCurrencyChange}/>
</>
)
}
Below is the select drop down component
const Select = ( props ) => {
return (
<>
<Form.Group>
<Form.Label>{props.label}</Form.Label>
<Form.Control as="select" size={props.size} value={props.value} custom onChange={(event) => props.handleCountryAndCurrencyChange(event.target.value)}>
{
props.List.map((item, index) => {
return <option key={item.code} value={item.code}>{item.name}</option>
})
}
</Form.Control>
</Form.Group>
</>
)
}
Expected :
When user selects on country selector drop down It should reflect on UI (User can see) but its state should not update. State only update only when user click on submit button
Thanks in advance
I need to be able to send two values when handling an onChange event in my select box however I am not sure a way of doing this as they are currently sent as a string. I am using react with react-bootstrap.
const DropDown = () => {
const handleOnchange = (event) => {
console.log(event.target.value)
const filter = event.target.value[0];
const initialState = [...initialValues]
let result = [];
console.log(event)
setDefaultSelect(event.target.name)
if(event.target.value === 'Show All') {
setValues(initialState);
}
else {
initialState.forEach(item => {
let found = item.store_category.filter(item => item == filter);
if (found.length) {
result.push(item);
}
});
setValues(result);
}
}
return (
<Form>
<Form.Group controlId="exampleForm.SelectCustom">
<Form.Control onChange={(e) => handleOnchange(e)} as="select" custom>
<option className="pl-2" hidden>{defaultSelect}</option>
<option>Show All</option>
{storeCategorys.map((item, index) => (
<option value={{itemId: item.id, itemName: item.name}} key={index}>{item.name}</option>
))}
</Form.Control>
</Form.Group>
</Form>
);
}
How about to use useEffect instead of handleOnchange?
const DropDown = () => {
const [selectedItem, setSelectedItem] = useState({});
useEffect(() => {
console.log(selectedItem);
}, [selectedItem]); // will be fired when `selectedItem` is changes
...
{storeCategorys.map((item, index) => (
<option
value={{itemId: item.id, itemName: item.name}}
key={index}
onClick={() => {setSelectedItem(item)}} // will fire `useEffect`
// on `selectedItem`
>{item.name}</option>
))}
...
}
i have a backend api build on nodejs. in the code the api return some categories array.
i ran .map() function on the array to display the array in the JSX.
after that i added a checkBox to each object inside the array.
so what im trying to do is if the checkbox is true its will added another h1 Element (JSX).
Only to the object i clicked on the checkbox.
i tryied to add "status" props and make it false or true and then catch it with onClick e.target.status?
"YES" : "NO"
also, i tried to added checkBox useState and make it true or false . and its work. but not as i want
its display Yes or No to the all objects and not only to the on i clicked on.
const Category = ({ history }) => {
const dispatch = useDispatch()
const user = useSelector((state) => state.waiter)
const selectedCategory = useSelector((state) => state.selectedTable)
const [currectCategory, setCurrectCategory] = useState([])
const [categoryName, setCategoryName] = useState("")
const [categoryIMG, setCategoryIMG] = useState("not found")
const [checkBox, setCheckBox] = useState("false")
useEffect(() => {
if (!user.name) {
history.push('/login')
} else {
(async () => {
const res = await fetch('http://localhost:1000/categories/' + selectedCategory)
const data = await res.json()
setCurrectCategory(data.CurrectCountry.subcategories.map(sc => sc.name))
setCategoryName(data.CurrectCountry.name)
setCategoryIMG(data.CurrectCountry.img)
})()
}
}, [user])
const goBack = () => {
dispatch({
type: 'ALL_CATEGORIES'
})
history.push('/login')
}
const handleCheck = (e) => {
setCheckBox(e.target.checked.toString())
console.log(e.target.checked)
}
return (
<>
<Button className="LogButton" color="secondary" onClick={goBack}>back</Button>
<div className="SingleCategory">
<h1>{categoryName}</h1>
<ListGroup>
{currectCategory.map(category => {
return (
<Row className="Col-padd" key={category}>
<div>
<InputGroup className="mb-3">
<b className="ItemName"> {category} </b>
<img src={categoryIMG} height="100" width="100" ></img>
<FormCheck id={category} className="Checkbox" onChange={handleCheck}></FormCheck>
{checkBox == "true" ? <b>yes</b> : <b>No</b>}
</InputGroup>
</div>
</Row>
)
})}
</ListGroup>
</div>
</>
)
}
Thanks for help !!
You are only creating a single value for the checkbox. If you want to show for all the checkbox, if you have to track the value for each checkbox shown below,
const [checkBox, setCheckBox] = useState({}); // checkBoxName: value
const handleCheck = (e) => {
setCheckBox((prev) => {...prev, [e.target.name]: e.target.value};
}
{!!checkBox['name'] === true ? <b>yes</b> : <b>No</b>}
//change the attribute according to your implementation.
Your problem is that you're just creating a single value for the checkbox and not separating the individual checkboxes. You could solve this in many different ways, but you would be well served by extracting the code for your checkbox to a separate component.
const Checkbox = ({ category, categoryIMG }) => {
const [isChecked, setIsChecked] = useState(false);
const handleCheck = () => {
setIsChecked((prevState) => !prevState);
};
return (
<Row className="Col-padd" key={category}>
<div>
<InputGroup className="mb-3">
<b className="ItemName"> {category} </b>
<img src={categoryIMG} height="100" width="100"></img>
<FormCheck id={category} className="Checkbox" onChange={handleCheck}></FormCheck>
{isChecked == 'true' ? <b>yes</b> : <b>No</b>}
</InputGroup>
</div>
</Row>
);
};
With a separate checkbox component like above you could instantiate it like this in the map:
<ListGroup>
{currectCategory.map((category) => (
<Checkbox category={category} categoryIMG={categoryIMG} />
))}
</ListGroup>
I have a function which gets a data from an API using axios. I've created a custom axios functional component to call which only work inside a function (it throws an Invalid hook call error when used on class).
I've searched several questions on this site but all seems to be using a class. So, is it possible to populate a second dropdown based on the selection?
PS. I'm using react-bootstrap. I tried to implement it but the second dropdown does not update.
Here is my code: (Please bear with the code. It's dirty )
import React, { useState, useEffect } from 'react';
import { Container, Row, Col, Button, Form } from 'react-bootstrap';
import Loader from '../Components/Loader';
import Err404 from '../Views/Err404';
import { GetRequest } from '../Hooks/GetRequest';
function Profile() {
let divs = GetRequest('/divisions')
let subs = GetRequest('/subjects')
const [selectedDiv, setSelectedDiv] = useState("")
const [selectedSub, setSelectedSub] = useState("")
let content = null
let subOptions = null
let divOptions = null
let subArr = null
let subFilter = null
let disabled = true
if (divs.error || subs.error) {
return (<Err404 />)
}
if (divs.loading || subs.loading) {
content = <Loader />
}
if (divs.data && subs.data) {
content = null
divOptions =
divs.data.map((div) => (
<option key={div._id}>{div.name}</option>
))
subArr =
subs.data.map((sub) => (
{
key: sub._id,
courseCode: sub.courseCode,
division: sub.division
}
))
subOptions =
subArr.map((sub) => (
<option key={sub.key}>{sub.courseCode}</option>
))
}
const handleDiv = (e) => {
disabled = false
setSelectedDiv(e.target.value);
console.log("selDiv: ", selectedDiv)
console.log("subArr: ", subArr)
subFilter =
subArr.filter((sub) => (
sub.division === selectedDiv
))
subOptions =
subFilter.map((sub) => (
<option key={sub.key}>{sub.courseCode}</option>
))
console.log("subOps: ", subOptions, "\nsubfil: ", subFilter)
}
const handleSub = (e) => {
console.log("selSub: ", e.target.value);
setSelectedSub(e.target.value);
}
return (
<Container fluid="sm" className="p-2">
{content ? content :
<Form>
<Row>
<Col>
<Form.Group controlId="exampleForm.ControlSelect1">
<Form.Label>Divison</Form.Label>
<Form.Control
as="select"
onChange={(e) => handleDiv(e)}
defaultValue="Choose ..."
>
<option disabled>Choose ...</option>
{divOptions}
</Form.Control>
</Form.Group>
</Col>
<Col>
<Form.Group controlId="exampleForm.ControlSelect1">
<Form.Label>Subject</Form.Label>
<Form.Control
as="select"
onChange={(e) => handleSub}
defaultValue="Choose ..."
disabled={disabled}
>
<option disabled>Choose ...</option>
{subOptions}
</Form.Control>
</Form.Group>
</Col>
</Row>
</Form>
}
</Container>
)
}
export default Profile
Managed to make it work
const handleDiv = (e) => {
disabled = false
setSelectedDiv(e.target.value);
}
if (subArr) {
console.log("selDiv: ", selectedDiv)
console.log("subArr: ", subArr)
subFilter =
subArr.filter((sub) => (
sub.division === selectedDiv
))
subOptions =
subFilter.map((sub) => (
<option key={sub.key}>{sub.courseCode}</option>
))
console.log("subOps: ", subOptions, "\nsubfil: ", subFilter)
}