Cannot use multiple radiogroups in react-materialize - reactjs

So, I am building an avatar creator for my application, and I have a Carousel that holds multiple RadioGroups, I can slide to the new radiogroup, but when I click on any of the other radio groups (except for the first one) and it will only target the value from the first one.
<Carousel options={{ fullWidth: true, indicators: true }} className="black-text center">
<div>
<RadioGroup
name="hair"
label="Hair Selection"
value={this.state.avatar.topType}
onChange={this.handleAvatarChange}
options={[{ label: 'Eyepatch', value: 'Eyepatch' }, { label: 'Long
Hair', value: 'LongHairStraight2' }, { label: 'Medium Hair', value:
'LongHairNotTooLong' }, { label: 'Short Hair', value:
'ShortHairShortFlat' }, { label: 'Short Dreads', value:
'ShortHairDreads01' }, { label: 'Balding', value: 'ShortHairSides'
}]}
/>
</div>
<div>
<RadioGroup
name="hairColor"
label="Hair Selection"
value={this.state.avatar.hairColor}
onChange={this.handleAvatarChange}
options={[{ label: 'Brown', value: 'Brown' }, { label: 'Blonde',
value: 'Blonde' }, { label: 'Red', value:
'Red' }, { label: 'Gray', value:
'Gray' }, { label: 'Black', value:
'Black' }, { label: 'Auburn', value: 'Auburn'
}]}
/>
</div>
</Carousel>
handleAvatarChange = (event) => {
let selection = event.target.value;
let type = event.target.name;
console.log(selection, 'event')
console.log(type, 'type');
let avatarCopy = { ...this.state.avatar };
if (type === 'hair') {
avatarCopy.topType = selection
this.setState({ avatar: avatarCopy });
} else if (type === 'hairColor') {
avatarCopy.hairColor = selection;
this.setState({ avatar: avatarCopy });
}
}
I expect that when I slide to the next slide on the carousel, that I can change the state based on the type that was passed back to my handleAvatarChange function. Instead, all of the slides are changing the values based on the first slide.

It seems like the autogenerated id numbering for the options resets to 0 for each radiogroup, even if they are in the same DOM.
Therefore, each n input of the radio groups has the same id (<input id="radio0" type="radio" value="1" checked="">).
I have reported the bug here.
As a workaround, I'm requesting an "id" prop, and if not found, concatenating the component's name to the index to build the id's.
var RadioGroup = function RadioGroup(_ref) {
var onChange = _ref.onChange,
withGap = _ref.withGap,
disabled = _ref.disabled,
name = _ref.name,
radioClassNames = _ref.radioClassNames,
value = _ref.value,
options = _ref.options;
return _react.default.createElement(_react.default.Fragment, null, options.map(function (radioItem, idx) {
return _react.default.createElement("label", {
className: radioClassNames,
htmlFor: radioItem.id ? radioItem.id : "radio".concat(radioItem.name).concat(idx),
key: radioItem.id ? radioItem.id : "radio".concat(radioItem.name).concat(idx)
}, _react.default.createElement("input", {
id: radioItem.id ? radioItem.id : "radio".concat(radioItem.name).concat(idx),
value: radioItem.value,
type: "radio",
checked: radioItem.value === value,
name: name,
onChange: onChange,
disabled: disabled,
className: (0, _classnames.default)({
'with-gap': withGap
})
}), _react.default.createElement("span", null, radioItem.label));
}));
};

Related

MUI Textfield does not update State

I have an app which used various inputs which is functioning. For example, I have an initial dataset which is built from an API request, see below:
const [userData, setuserData] = useState([])
const companyuser = useSelector(state=>state.companyuser.currentUser)
useEffect(()=> {
const getUserData = async ()=>{
try{
const companyResponse = await userRequest.get(`companyprofile/findCompany/${companyuser._id}`);
setuserData(companyResponse.data.others)
}catch(err){}
};
getUserData()
},[])
const userInputDataSchema = [
{
id: 1,
label: "companyTitle",
type: "companyTitle",
placeholder: userData.companyTitle,
},
{
id: 2,
label: "surname",
type: "surname",
placeholder: userData.surname
},
{
id: 3,
label: "Email",
type: "email",
placeholder: userData.email
},
{
id: 4,
label: "Position",
type: "position",
placeholder: userData.position
},
{
id: 5,
label: "User Image",
type: "image",
placeholder: userData.userImage
},
{
id: 6,
label: "Professional Bio",
type: "professionalBio",
placeholder: userData.employees
},
{
id: 7,
label: "locationCity",
type: "locationCity",
placeholder: userData.locationCity
},
{
id: 8,
label: "locationCountry",
type: "locationCountry",
placeholder: userData.locationCountry
},
{
id: 9,
label: "whyWork_1",
type: "whyWork_1",
placeholder: userData.whyWork_1
},
];
This data is then mapped across the app, and will update when used. For example:
<UpdateUserDetailsSingular>
{userInputDataSchema.map((input) => (
<FormInput className="formInput" key={input.companyTitle}>
{input.id == 1 ?
<UserInput type={input.type} name="companyTitle" placeholder={input.placeholder}
onChange={handleChange} />
: null}
</FormInput>
))}
</UpdateUserDetailsSingular>
This is functioning. When I use the MUI larger input textfield, it does not update my state. It will dispaly the placeholder text, but if you type it will not handle it.
What is the reason?
{userInputDataSchema.map((input) => (
<div>
{input.id == 9 ?
<TextField
name="whyWork_1"
label="Diversity & Inclusion at Australia Post"
multiline
rows={15}
defaultValue={input.placeholder}
key={input.placeholder}
fullWidth
fullHeight
type={input.type}
handleChange={handleChange}
/> : null}
</div>
))}
</InputBoxContainer>
Does
The reason for this is could be a Mui related misconfiguration in TextField.
defaultValue any The default value. Use when the component is not controlled.
value any The value of the input element, required for a controlled component.
switching from defaultValue to value should do the job.

update react state of nested object with array

I want to insert a new object inside the options which is an array property inside the select1 object every time the user clicks on add button, how can I achieve this target, pls reply to this query.
I want to build a simple application where a user enters the options in the select box and can apply conditions based upon the selected option.
const [select1, setSelect1] = useState({
id: uuid(),
type: 'select',
properties: {
label: 'Select1',
options: [
{
id: uuid(),
label: 'text1',
value: {
labelTxt: 'text1',
childOptions: [],
},
},
{
id: uuid(),
label: 'text2',
value: {
labelTxt: 'text2',
childOptions: [],
},
},
],
},
parentId: null,
});
function addOptionsVal() {
setSelect1((prevState) => {
const options = [...prevState.properties.options];
options.splice(1, 0, {
id: uuid(),
label: optionVal,
value: {
labelTxt: optionVal,
childOptions: [],
},
});
console.log(options);
return { ...prevState, options };
});
}
return (
<div>
<select name="" id="">
<option value="">--select--</option>
{select1.properties.options.map((option) => {
return <option>{option.label}</option>;
})}
</select>
</div>
<input
type="text"
value={optionVal}
onChange={(e) => handleValueChange(e)}
/>
<button onClick={addOptionsVal}>Add options</button>
</div>
<div>
<input
type="checkbox"
value="male"
checked={checked}
onChange={toggleCondition}
id="condition"
/>
<label htmlFor="condition">Apply condition</label>
</div>
)
This is the entire code file in case you need
Try this:
function addOptionsVal() {
setSelect1((prevState) => {
return {
...prevState,
properties: {
...prevState.properties,
options: [
...prevState.properties.options,
{
id: uuid(),
label: optionVal,
value: {
labelTxt: optionVal,
childOptions: [],
},
},
],
},
};
});
}
stackblitz
Also, take a look at this question Correct modification of state arrays in React.js

Two React-Select dropdowns resetting each other

I have two dropdown menus using React-Select:
<Select
className="type-select"
options={type_options}
onChange={filterType}
/>
<label id='filter-label'>Filter By Generation:</label>
<Select
className="gen-select"
options={generation_options}
onChange={filterGen}
/>
and the options for the two of these are as follows:
{ value: 'none', label: 'None'},
{ value: 'normal', label: 'Normal' },
{ value: 'fire', label: 'Fire' },
{ value: 'water', label: 'Water' },
{ value: 'grass', label: 'Grass' },
{ value: 'electric', label: 'Electric' },
{ value: 'ice', label: 'Ice' },
{ value: 'fighting', label: 'Fighting' },
{ value: 'poison', label: 'Poison' },
{ value: 'ground', label: 'Ground' },
{ value: 'flying', label: 'Flying' },
{ value: 'psychic', label: 'Psychic' },
{ value: 'bug', label: 'Bug' },
{ value: 'rock', label: 'Rock' },
{ value: 'ghost', label: 'Ghost' },
{ value: 'dark', label: 'Dark' },
{ value: 'dragon', label: 'Dragon' },
{ value: 'steel', label: 'Steel' },
{ value: 'fairy', label: 'Fairy' },
];
const generation_options = [
{ value: 'none', label: 'None'},
{ value: 'i', label: 'I'},
{ value: 'ii', label: 'II'},
{ value: 'iii', label: 'III'},
{ value: 'iv', label: 'IV'},
{ value: 'v', label: 'V'},
{ value: 'vi', label: 'VI'},
{ value: 'vii', label: 'VII'},
{ value: 'viii', label: 'VIII'},
]
Both of these dropdowns are stored in state using React Hooks, and in the onChange methods for both, the value for the given Select is updated and the other is reset to "none". Ex: If my first select is set to "Fire" then when I change my second select to Gen IV, the first select sets back to "none". Functionally, I already have this working and am able to use the React-Selects as I want, but the issue I'm having is that when updating one select, the other select won't change to show that it's been changed. I've tried adding value={type} where type is the state the first select corresponds to, but that just causes the React-Select component to never show the selected value. Does anyone know how to get these React-Selects to operate in the way I'm describing? I can elaborate on parts of this more if more explanation is needed.
Thanks!
Edit: Here are the filterType and filterGen methods:
function filterType(selectedOption) {
setType(selectedOption.value)
setGen("none")
if(selectedOption.value === "none") {
setPage(beginningUrl)
} else {
setPage(`https://pokeapi.co/api/v2/type/${selectedOption.value}`)
}
}
function filterGen(selectedOption) {
setGen(selectedOption.value)
setType("none")
if(selectedOption.value === "none") {
setPage(beginningUrl)
} else {
setPage(`generation-${selectedOption.value}`)
}
}
Figured it out. The issue was I was setting the state to just the value of the selected item, instead of the whole object (ie: {value: 'fire', label: 'Fire}. Fixed pieces of code below, with the changes in bold:
function filterType(selectedOption) {
**setType(selectedOption)**
**setGen({ value: 'none', label: 'None'})**
if(selectedOption.value === "none") {
setPage(beginningUrl)
} else {
setPage(`https://pokeapi.co/api/v2/type/${selectedOption.value}`)
}
}
function filterGen(selectedOption) {
**setGen(selectedOption)**
**setType({ value: 'none', label: 'None'})**
if(selectedOption.value === "none") {
setPage(beginningUrl)
} else {
setPage(`generation-${selectedOption.value}`)
}
}
<div className="select-container">
<label id='filter-label'>Filter By Type:</label>
<Select
className="type-select"
options={type_options}
onChange={filterType}
**value={type}**
/>
<label id='filter-label'>Filter By Generation:</label>
<Select
className="gen-select"
options={generation_options}
onChange={filterGen}
**value={gen}**
/>
</div>

How to count selected checkboxes in React functional component?

I am needing to add bit of text in the sidebar of the following code. In each section, I need to have a count of the number of checkboxes selected. Is it at all possible to do this in a functional component as I have? Any examples that I have found so far are only for class components. I would like to keep it as a functional component if possible.
I have the code below, but here is a working version too:
https://codesandbox.io/s/react-playground-forked-jgmof?file=/index.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import styled from "styled-components";
import { categoryData } from "./data";
const Menu = (props) => {
const [showPanel, togglePanel] = useState(false);
return (
<MenuWrapper>
<p>Showing 20 results</p>
<div className="button-wrapper">
<p>Filter By</p>
{categoryData.map((categorybutton, i) => (
<div key={i}>
<button key={i} onClick={() => togglePanel(!showPanel)}>
{categorybutton.category}
</button>
</div>
))}
<div>
{showPanel && (
<Sidebar>
{categoryData.map((categorysection, i) => (
<details className="dropdown-header">
<summary>{categorysection.category}</summary>
{categorysection.data.map((categorylabel, i) => (
<div key={i}>
<input
type="checkbox"
id="vehicle1"
name="vehicle1"
value="Bike"
/>
<label for="vehicle1">{categorylabel.label}</label>
</div>
))}
</details>
))}
</Sidebar>
)}
</div>
<p>Toggle</p>
<p>Clear all filters</p>
</div>
</MenuWrapper>
);
};
const MenuWrapper = styled.div`
width: 90vw;
display: flex;
justify-content: space-between;
.button-wrapper {
display: flex;
}
`;
const Sidebar = styled.div`
position: fixed;
width: 200px;
height: 100vh;
background: white;
top: 0;
left: 0;
z-index: 100000;
text-align: left;
.dropdown-header {
background: #f7f7f7;
margin: 20px;
}
`;
ReactDOM.render(<Menu />, document.getElementById("container"));
export const categoryData = [
{
category: "user",
data: [
{ checked: false, value: "Me", label: "Me" },
{ checked: false, value: "Kids", label: "Kids" },
{ checked: false, value: "Guestroom", label: "Guestroom" }
]
},
{
category: "comfort",
data: [
{ checked: false, value: "Ultra-Soft", label: "Ultra-Soft" },
{ checked: false, value: "Soft", label: "Soft" },
{ checked: false, value: "Medium", label: "Medium" },
{ checked: false, value: "Medium-Firm", label: "Medium-Firm" },
{ checked: false, value: "Firm", label: "Firm" },
{ checked: false, value: "Ultra-Firm", label: "Ultra-Firm" },
{ checked: false, value: "Unsure", label: "Unsure" }
]
},
{
category: "type",
data: [
{ checked: false, value: "Pillow Top", label: "Pillow Top" },
{ checked: false, value: "Open Coil", label: "Open Coil" },
{ checked: false, value: "Pocketed Coil", label: "Pocketed Coil" },
{ checked: false, value: "Quantum Coil", label: "Quantum Coil" },
{ checked: false, value: "Memory Foam", label: "Memory Foam" },
{ checked: false, value: "Latex", label: "Latex" },
{ checked: false, value: "Unsure", label: "Unsure" }
]
},
{
category: "budget",
data: [
{ checked: false, value: 500, label: "Under $500" },
{ checked: false, value: 1000, label: "$501 - $1000" },
{ checked: false, value: 1600, label: "1001 - $1600" },
{ checked: false, value: 2500, label: "$1601 - $2500" },
{ checked: false, value: 2501, label: "$2500 and up" },
{ checked: false, value: "Unsure", label: "Unsure" }
]
}
];
If I understand correctly, you wan't to show the total number of selected items on each category.
It would be easy if we'll create a new component for the section that will have it's own state tracking its selected items.
Let's call it CategorySection. It would then have a selected state that will be an array (empty by default) of its selected items. To update our selected state, we have to fireup a function everytime any of the checkbox is changed — if the checkbox is checked, we add the current item to our selected state otherwise it will be removed.
Then to display the total selected items, we can simply count the length of our selected state.
function CategorySection({ categorysection }) {
const [selected, setSelected] = useState([]);
function onChange(event, item) {
if (event.target.checked) {
setSelected([...selected, item]);
} else {
setSelected((prev) =>
prev.filter((currItem) => currItem.value !== item.value)
);
}
}
return (
<details className="dropdown-header">
<summary>
{categorysection.category}{" "}
{selected.length > 0 ? selected.length : null}
</summary>
{categorysection.data.map((categorylabel, i) => (
<div key={i}>
<input
type="checkbox"
id={categorylabel.value}
name="vehicle1"
value="Bike"
onChange={(event) => onChange(event, categorylabel)}
/>
<label for={categorylabel.value}>{categorylabel.label}</label>
</div>
))}
</details>
);
}
PS: You should have a unique id for every checkbox on your page.

How to customize only one option from react-select?

I'm working with react-select and I want to customize only one option from the drop-down. Is there such an opportunity? I would like to do something like:
const CustomOption = ({ innerRef, innerProps, data }) => data.custom
? (<div ref={innerRef} {...innerProps} >I'm a custom link</div>)
: defaultOne //<--- here I would like to keep default option
<ReactSelect
components={{ Option: CustomOption }}
options={[
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
{ custom: true },
]}
/>
Any thoughts how to achive that?
Your feeling is good, you can achieve your goal with the following way:
const CustomOption = props => {
const { data, innerRef, innerProps } = props;
return data.custom ? (
<div ref={innerRef} {...innerProps}>
I'm a custom link
</div>
) : (
<components.Option {...props} />
);
};
const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" },
{ custom: true }
];
function App() {
return <Select components={{ Option: CustomOption }} options={options} />;
}
The important thing to notice is to pass the entire props property to the components.Option to have the default behaviour.
Here a live example.

Resources