Using antd, I have a simple RadioGroup composed of three RadioButtons. Everything works I expect.
Additionally, I defined a button which is supposed to clear the selected button and revert the RadioGroup to the default. It is not working. The underlying data is OK, but the default RadioButton is not visually checked.
The rightmost RadioButton is the default. Selecting either of the other two and pressing the Clear button does not check the rightmost RadioButton.
I have looked at this issue for days and do not see my error. The code is a bit noisy because a database is invovled.
Codesandbox
import React, { useState, useEffect, Fragment } from 'react';
import { Radio } from 'antd';
import { ConsoleLog } from './consoleLog';
const RadioGroup = Radio.Group;
const App = () => {
const [filters, setFilters] = useState(false);
const initFilters = {
wireSizeId: { displayValue: '', type: 'num', dbKey: '' },
colorId: { displayValue: '', type: 'num', dbKey: '' },
insulationId: { displayValue: '', type: 'num', dbKey: '' },
wireTypeId: { displayValue: '', type: 'num', dbKey: '' },
solidStranded: { displayValue: '', type: 'num', dbKey: null },
copperAlum: { displayValue: '', type: 'num', dbKey: null },
};
useEffect(() => {
setFilters(initFilters);
}, []);
useEffect(() => {
if (filters) {
console.log('e1', filters);
}}, [filters]);
const handleClearFilters = () => {
console.log('clear1', filters);
setFilters(initFilters);
};
const handleCheckbox = (props) => {
console.log('checkbox', filters);
console.log('checkbox', props.target.name, props.target.value, props.target.id);
let updated = {};
updated[props.target.name] =
{ displayValue: props.target.id, type: 'num', dbKey: props.target.value };
console.log(updated);
setFilters({...filters, ...updated});
};
return (
(filters?<Fragment>
<ConsoleLog>Render</ConsoleLog>
<div>
<ConsoleLog>Radio {filters['solidStranded'].dbKey}</ConsoleLog>
<RadioGroup
name='solidStranded'
onChange={handleCheckbox}
defaultValue=''
>
<Radio
value='0'
id='Solid'
checked={filters['solidStranded'].dbKey === '0'}
>
Solid
</Radio>
<Radio
value='1'
id='Stranded'
checked={filters['solidStranded'].dbKey === '1'}
>
Stranded
</Radio>
<Radio
value=''
id='none'
checked={filters['solidStranded'].dbKey === null}
>
None
</Radio>
</RadioGroup>
</div>
<div>
<button onClick={handleClearFilters} >
Clear Filters
</button>
</div>
</Fragment>:null)
);
};
export default App;
Use value in RadioGroup instead checked attribute, value must be not null-able
import React, { useState, Fragment } from "react";
import { Radio } from "antd";
const RadioGroup = Radio.Group;
const App = () => {
const initFilters = {
wireSizeId: { displayValue: "", type: "num", dbKey: "" },
colorId: { displayValue: "", type: "num", dbKey: "" },
insulationId: { displayValue: "", type: "num", dbKey: "" },
wireTypeId: { displayValue: "", type: "num", dbKey: "" },
solidStranded: { displayValue: "", type: "num", dbKey: "-1" }, // change
copperAlum: { displayValue: "", type: "num", dbKey: null }
};
const [filters, setFilters] = useState(initFilters);
const handleClearFilters = () => {
setFilters(initFilters);
};
const handleCheckbox = (props) => {
setFilters({
...filters,
solidStranded: {
// change
displayValue: props.target.id,
type: "num",
dbKey: props.target.value
}
});
};
// the ternary operator isn't necessary now.
return filters ? (
<Fragment>
<div>
<RadioGroup
name="solidStranded"
onChange={handleCheckbox}
value={filters["solidStranded"].dbKey} // change
>
<Radio value="0" id="Solid">
Solid
</Radio>
<Radio value="1" id="Stranded">
Stranded
</Radio>
<Radio value="-1" id="none">
None
</Radio>
</RadioGroup>
</div>
<div>
<button onClick={handleClearFilters}>Clear Filters</button>
</div>
</Fragment>
) : null;
};
export default App;
https://codesandbox.io/s/chekbox-example-8vh07?file=/src/App.js
Related
I have some trouble with my react-select: When I click 'Submit', it save an object that have both 'value' and 'label' like this:
enter image description here
All I need is when I choose, it's show label list, and when I submit, it save only value. What can I do? Here are my code:
const [mainLang, setMainLang] = useState("");
const mainLangOptions = [
{ value: 'vi', label: 'Vietnamese' },
{ value: 'en', label: 'English' },
{ value: 'zh', label: 'Chinese' },
{ value: 'ja', label: 'Japanese' },
{ value: 'de', label: 'German' },
];
//This is Select part
<Select
onChange={(e) =>setMainLang(e)}
options={mainLangOptions}
/>
You need to set the option value as the mainLangOptions.value and the label as
mainLangOptions.label. By doing that you will display the label as option labels and save the value as the value of option tag. Check out the code below :
import React from "react";
import "./styles.css";
class App extends React.Component {
constructor() {
super();
this.state = {
mainLanguage: ""
};
}
onOptionChangeHandler = (event) => {
this.state.mainLanguage = event.target.value;
console.log(this.state.mainLanguage);
};
render() {
const mainLangOptions = [
{ value: "vi", label: "Vietnamese" },
{ value: "en", label: "English" },
{ value: "zh", label: "Chinese" },
{ value: "ja", label: "Japanese" },
{ value: "de", label: "German" }
];
return (
<div>
<select onChange={this.onOptionChangeHandler}>
<option>Please choose one option</option>
{mainLangOptions.map((option, index) => {
return (
<option value={option.value} key={index}>
{option.label}
</option>
);
})}
</select>
</div>
);
}
}
export default App;
You must use e.target.value inside your setMainLang
<Select onChange={(e) => setMainLang(e.target.value)}>
This should work for your code but if it doesn't work, here is a complete code that I have tested in code sandbox and you can try it.
import React, { useState } from "react";
import { Select } from "#chakra-ui/react";
const Users = () => {
const [mainLang, setMainLang] = useState("");
const mainLangOptions = [
{ value: "vi", label: "Vietnamese" },
{ value: "en", label: "English" },
{ value: "zh", label: "Chinese" },
{ value: "ja", label: "Japanese" },
{ value: "de", label: "German" }
];
return (
<>
<Select onChange={(e) => setMainLang(e.target.value)}>
{mainLangOptions.map((op) => (
<option value={op.value}>{op.label}</option>
))}
</Select>
<h1>{mainLang}</h1>
</>
);
};
export default Users;
I have a form that have several input fields and for some reason my component y re-rendering everytime y change the value of my input field which produces to the input to lose focus.
ContactForm.js:
const ContactForm = () => {
const [values, setValues ] = useState({
name: '',
lastname: '',
email: '',
confirmEmail: '',
message: ''
});
const inputs = [
{
id: Math.random(),
name: 'name',
type: 'text',
placeholder: 'Name'
},
{
id: Math.random(),
name: 'lastname',
type: 'text',
placeholder: 'Last Name'
},
{
id: Math.random(),
name: 'email',
type: 'email',
placeholder: 'Email'
},
{
id: Math.random(),
name: 'confirmEmail',
type: 'email',
placeholder: 'Confirm Email'
},
{
id: Math.random(),
name: 'message',
type: 'text',
placeholder: 'Message'
}
]
const handleSubmit = (e) => {
e.preventDefault();
}
MY child component, FormInput.js:
import React from 'react'
import './FormInput.css';
/* import { Input } from '../atoms/Input'; */
const FormInput = (props) => {
const { id, onChange, ...inputProps } = props;
return (
<div className='formInput'>
{/* <label htmlFor="">Username</label> */}
{/* <Input {...inputProps} onChange={onChange}/> */}
<input {...inputProps} onChange={onChange} />
</div>
)
}
export default FormInput
const onChange = (e) => {
setValues({...values, [e.target.name]: e.target.value});
}
console.log(values);
return (
<form className='contactForm' onSubmit={handleSubmit}>
{inputs.map((input) => (
<FormInput
key={input.id}
{...input}
value={values[input.name]}
onChange={onChange}
/>
))}
<SubmitBtn/>
</form>
)
}
So is there a solution for this, so that my input field doesn´t lose focus after re-rendering? Or should i prevent re-rendering?
you have 3 options here.
move the input array outside of the component so that it is always the same on every iteration. But if you are fetching this from the server, that is not possible.
you can use a useMemo hook on the input array and make sure to pass an empty array as a dependency array.
remove the Math.random function and maybe use a unique id from the server or for the time being you can use the array index (even though it is not advisable).
I have created a small POC. if you remove the useMemo, the input(s) will lose their focus on every re-render.
Following is the code:
import * as React from 'react';
import './style.css';
export default function App() {
const inputs = React.useMemo(
() => [
{
id: Math.random(),
name: 'name',
type: 'text',
placeholder: 'Name',
},
{
id: Math.random(),
name: 'lastname',
type: 'text',
placeholder: 'Last Name',
},
{
id: Math.random(),
name: 'email',
type: 'email',
placeholder: 'Email',
},
{
id: Math.random(),
name: 'confirmEmail',
type: 'email',
placeholder: 'Confirm Email',
},
{
id: Math.random(),
name: 'message',
type: 'text',
placeholder: 'Message',
},
],
[]
);
const [state, setState] = React.useState({
name: '',
email: '',
message: '',
confirmEmail: '',
lastname: '',
});
const handleChange = (e: any) => {
const value = (e.target as HTMLInputElement).value;
const name = (e.target as HTMLInputElement).name;
setState({
...state,
[name]: value,
});
};
const handleSubmit = () => {
console.log('state', state);
};
return (
<div>
{inputs.map((item) => (
<div key={item.id}>
<label>{item.name}: </label>
<input
name={item.name}
onChange={handleChange}
placeholder={item.placeholder}
/>
</div>
))}
<button onClick={handleSubmit}>Submit</button>
</div>
);
}
It's probably because you are calling Math.random in the body of the ContactForm component. You should never call Math.random() during rendering.
In your case, you can probably move the const inputs to outside the component.
on react-data-grid 7.0.0-beta
I read through the most recent demos provided in git repo for react-data-grid and implemented a custom dropdown for my use case.
Dropdown seems to be working but it is not updating the grid data upon selection. The editable property doesn't seem to be working either.
test code is implemented here:
Sandbox: https://codesandbox.io/s/react-data-grid-custom-dropdown-editor-kcy5n
export const EntryCriteriaGrid = () => {
const columns = [
{
key: "r1",
name: "Criteria",
width: "50%",
resizable: true,
editable: true
},
{
key: "status",
name: "Status",
editor: DropdownCustomEditor,
editorOptions: {
editOnClick: true
},
editable: true
},
{ key: "tracker", name: "Tracker", editable: true }
];
const rows = [
{ r1: "data 1", status: "BLOCKED", tracker: "tracker 1" },
{ r1: "data 2", status: "PASS", tracker: "tracker 1" },
{ r1: "data 3", status: "ISSUE", tracker: "tracker 2" }
];
const [state, setState] = useState({ rows });
const onGridRowsUpdated = ({ fromRow, toRow, updated }) => {
setState((state) => {
const rows = state.rows.slice();
for (let i = fromRow; i <= toRow; i++) {
rows[i] = { ...rows[i], ...updated };
}
return { rows };
});
};
return (
<div>
<ReactDataGrid
columns={columns}
rows={state.rows}
rowsCount={3}
onGridRowsUpdated={onGridRowsUpdated}
enableCellSelect={true}
className="rdg-light"
/>
</div>
);
};
export default EntryCriteriaGrid;
import React, { Component } from "react";
import ReactDOM from "react-dom";
export default class DropdownCustomEditor extends Component {
constructor(props) {
super(props);
this.state = {
selected: ""
};
this.options = [
{ id: "blocked", value: "BLOCKED" },
{ id: "pass", value: "PASS" },
{ id: "issue", value: "ISSUE" },
{ id: "notStarted", value: "NOT STARTED" }
];
}
componentDidMount() {
if (this.props.row && this.props.row.status)
this.setState({ selected: this.props.row.status });
}
getValue = function () {
return { status: this.state.selected };
};
getInputNode() {
return ReactDOM.findDOMNode(this).getElementsByTagName("select")[0];
}
update(e) {
this.setState({ selected: e.target.value });
this.props.onRowChange({ ...this.props.row, status: e.target.value }, true);
}
render() {
return (
<select
className="rdg-select-editor"
onChange={(e) => this.update(e)}
autoFocus
value={this.state.selected}
>
{this.options.map((elem) => {
return (
<option key={elem.id} value={elem.value}>
{elem.value}
</option>
);
})}
</select>
);
}
}
Just change your code as follows:
In DropdownCustomEditor component:
update(e) {
this.setState({ selected: e.target.value });
this.props.onRowChange({ ...this.props.row, status: e.target.value });
}
In EntryCriteriaGrid component
const onGridRowsUpdated = (rows) => {
setState({ rows });
};
and
<ReactDataGrid
columns={columns}
rows={state.rows}
rowsCount={3}
//onRowsUpdate={onGridRowsUpdated}
enableCellSelect={true}
className="rdg-light"
onRowsChange={(rows) => onGridRowsUpdated(rows)}
/>
I want to have a drop down in my application which allows the user to add an item to the dropdown. I am using React Sematic UI.
Sematic UI Dropdown ALlowAdditions
I am new to react hooks and I want to know how I can implement the onChange and onAddition function using hooks.
import React, { Component } from 'react'
import { Dropdown } from 'semantic-ui-react'
const options = [
{ key: 'English', text: 'English', value: 'English' },
{ key: 'French', text: 'French', value: 'French' },
{ key: 'Spanish', text: 'Spanish', value: 'Spanish' },
{ key: 'German', text: 'German', value: 'German' },
{ key: 'Chinese', text: 'Chinese', value: 'Chinese' },
]
class DropdownExampleAllowAdditions extends Component {
state = { options }
handleAddition = (e, { value }) => {
this.setState((prevState) => ({
options: [{ text: value, value }, ...prevState.options],
}))
}
handleChange = (e, { value }) => this.setState({ currentValue: value })
render() {
const { currentValue } = this.state
return (
<Dropdown
options={this.state.options}
placeholder='Choose Language'
search
selection
fluid
allowAdditions
value={currentValue}
onAddItem={this.handleAddition}
onChange={this.handleChange}
/>
)
}
}
export default DropdownExampleAllowAdditions
Any help would be greatly appreciated. Thanks in advance :)
import React, { useState } from "react";
import { Dropdown } from "semantic-ui-react";
const options = [
{ key: "English", text: "English", value: "English" },
{ key: "French", text: "French", value: "French" },
{ key: "Spanish", text: "Spanish", value: "Spanish" },
{ key: "German", text: "German", value: "German" },
{ key: "Chinese", text: "Chinese", value: "Chinese" }
];
const DropDownWithHooks = () => {
const [dropDownOptions, setDropDownOptions] = useState(options);
const [currentValue, setCurrentValue] = useState("");
const handleAddition = (e, { value }) => {
setDropDownOptions((prevOptions) => [
{ text: value, value },
...prevOptions
]);
};
const handleChange = (e, { value }) => setCurrentValue(value);
return (
<Dropdown
options={dropDownOptions}
placeholder="Choose Language"
search
selection
fluid
allowAdditions
value={currentValue}
onAddItem={handleAddition}
onChange={handleChange}
/>
);
};
export default DropDownWithHooks;
Working Sandbox
I am using useState hook for my add product form.
When I refresh my page, data is not displaying for the field category (I am trying to display categories, so the user can select category from the list to create a product for that category). But! Data keeping in redux store:
enter image description here
It only shows when I go to another page using react router() and then go back.
This is my code:
export const AddProduct = () => {
const dispatch = useDispatch();
const userId = useSelector(state => state.auth.userId);
const categories = useSelector(state => state.categories.categories);
const [avatar, setAvatar] = React.useState('');
React.useEffect(() => {
dispatch(categoriesActions.fetchCategories());
}, [dispatch]);
const [orderForm, setOrderForm] = React.useState({
title: {
elementType: 'input',
label: 'Title',
elementConfig: {
type: 'text',
placeholder: 'Title'
},
value: '',
validation: {
required: true
},
valid: false,
touched: false
},
price: {
elementType: 'input',
label: 'Price',
elementConfig: {
type: 'text',
placeholder: 'Price'
},
value: '',
validation: {
required: true
},
valid: false,
touched: false
},
description: {
elementType: 'textarea',
label: 'Description',
elementConfig: {
type: 'text',
placeholder: 'Description'
},
value: '',
validation: {
required: true
},
valid: false,
touched: false
},
category: {
elementType: 'select',
label: 'Select category',
elementConfig:
categories.map(category => (
<option key={category._id} value={category.title}>
{category.title}
</option>))
,
value: '',
validation: {
required: true
},
valid: false,
touched: false
},
});
const [formIsValid, setFormIsValid] = React.useState(false);
const addProductData = event => {
event.preventDefault();
const formData = {};
for (let formElementIdentifier in orderForm) {
formData[formElementIdentifier] = orderForm[formElementIdentifier].value;
}
const product = {
userId: userId,
title: formData.title,
price: formData.price,
description: formData.description,
category: formData.category
}
dispatch(productsActions.addProduct(product));
}
const inputChangedHandler = (event, inputIdentifier) => {
const updatedFormElement = updateObject(orderForm[inputIdentifier], {
value: event.target.value,
valid: checkValidity(
event.target.value,
orderForm[inputIdentifier].validation
),
touched: true
});
const updatedOrderForm = updateObject(orderForm, {
[inputIdentifier]: updatedFormElement
});
let formIsValid = true;
for (let inputIdentifier in updatedOrderForm) {
formIsValid = updatedOrderForm[inputIdentifier].valid && formIsValid;
}
setOrderForm(updatedOrderForm);
setFormIsValid(formIsValid);
};
const formElementsArray = [];
for (let key in orderForm) {
formElementsArray.push({
id: key,
config: orderForm[key]
});
}
let form = (
<form onSubmit={addProductData}>
{formElementsArray.map(formElement => (
<Input
key={formElement.id}
elementType={formElement.config.elementType}
elementConfig={formElement.config.elementConfig}
value={formElement.config.value}
label={formElement.config.label}
hint={formElement.config.hint}
invalid={!formElement.config.valid}
shouldValidate={formElement.config.validation}
touched={formElement.config.touched}
changed={event => inputChangedHandler(event, formElement.id)}
/>
))}
<Button btnType="Success" disabled={!formIsValid}>ORDER</Button>
</form>
)
return (
<div class="wrapper">
<Header />
<article class="main">
<div class="row">
<div class="item--1-4 image-block">
<div class="product-image-group">
<img class="product-image-big" src={`/${avatar}`} />
<hr class="border-divider" />
<input type="file" onChange={e => setAvatar(e.target.files[0].name)} name="imageUrl" id="imageUrl" />
</div>
</div>
<div class="item--3-4">
<div class="item-title">
<h3>Add product</h3>
<hr class="border-divider" />
</div>
{form}
</div>
</div>
</article>
<LeftMenu />
</div>
)
}
This is line in my code related to that select field:
elementConfig:
categories.map(category => (
<option key={category._id} value={category.title}>
{category.title}
</option>))
That's because the first time that component loads, there is nothing in the categories and when the categories are set, you're not setting the orderForm data again. this is called stale props you need to do this:
useEffect(() => {
setOrderForm((oldValue) => ({
...oldValue,
category: {
...oldValue.category,
elementConfig: categories.map((category) => (
<option key={category._id} value={category.title}>
{category.title}
</option>
)),
},
}));
}, [categories])
This way every time categories data is changed you changing the orderForm state accordingly