I can't hanbleChange with react-select - reactjs

I want to change it by using contact-select, but I keep getting an error.
When I select this option, I can see this error. help me plz.
error
this is options
const options = [
{ value: 'descript', label: '주관식' },
{ value: 'choice', label: '객관식' },
{ value: 'combine', label: '다중식' }
];
and this is onChange fuction
onChangeTmpType = (e) => {
this.setState({
tmp_type: e.target.value
})
}
this is React-Select
<Select
components={makeAnimated()}
value={this.state.tmp_type}
onChange={this.onChangeTmpType}
options={options}
/>

As specified in the documentation here, the onChange function looks like this:
function (
One of <
Object,
Array<Object>,
null,
undefined
>,
{
action required One of <
"select-option",
"deselect-option",
"remove-value",
"pop-value",
"set-value",
"clear",
"create-option"
>
}
) => undefined
and the e const you declare actually has the following structure:
{
label: ...,
value: ...
}
so no target key here but directly e.value if what you want is to access the props value.
Here a live example with console.log so you can see what's happening.

Replace your onChangeTmpType function as follows.
onChangeTmpType = (e) => {
this.setState({
tmp_type: e.value
})
}
The reason for this as (#Laura mentioned before) is that the e from react select only contains the value and the label .

Related

Formik does not get date value when it returns from function

I created an array of objects that has form field definitions in order to quickly generate dynamic user forms, in react.
export const formFieldList = [
{
fieldType: 'input',
inputType: 'date',
name: 'LastLoginDate',
id: 'LastLoginDate',
label: 'Last Login Date',
value: moment(new Date()).format('YYYY-MM-DD'),
classWrapper: 'col-md-3',
}]
The problem occurred when I changed this constant array to a function. I need to get some data from async functions so I changed this array to a async func as well. All other form fields (text, select, checkbox) still work well but date fields, formik doest get date value after then.
export const formFieldList = async () => ([
{
fieldType: 'input',
inputType: 'date',
name: 'LastLoginDate',
id: 'LastLoginDate',
label: 'Last Login Date',
value: moment(new Date()).format('YYYY-MM-DD'),
classWrapper: 'col-md-3',
},
{
fieldType: 'select',
name: 'CurrencyId',
id: 'CurrencyId',
label: 'Currencies',
classWrapper: 'col-md-3',
options: await getCurrencies()
}]
If I write date value as hard-coded, it doesnt change anything, I mean function returns everything well, the problem is formik doesnt get that value as a prop when it returns from a function. My input component that get that values:
<input
{...formik.getFieldProps(Field.name)}
type={Field.inputType}
placeholder={Field.placeholder}
name={Field.name}
id={Field.id || Field.name}
/>
In the DOM, input element is shown as <input value name='xx' ... />.
My FormGenerator component that takes formFieldList and returns it to form:
const FormGenerator:FC<Props> = ({formFieldList, formButton, onSubmitValues}) => {
...
let initialValues = {}
for (const Field of formFieldList) {
if (Field.name !== 'newLine'){
initialValues = {...initialValues, [Field.name]: Field.value}
}
}
const schema = formFieldList.reduce(getValidationSchema, {})
const validationSchema = Yup.object().shape(schema)
const formik = useFormik({
initialValues,
enableReinitialize: true,
validationSchema,
onSubmit: async (values, {setStatus, setSubmitting}) => {
setLoading(true)
try {
onSubmitValues(values)
setLoading(false)
} catch (error) {
console.error(error)
setStatus('Some errors occurred')
setSubmitting(false)
setLoading(false)
}
},
})
return (
...
<form
onSubmit={formik.handleSubmit}
noValidate
>
{ formFieldList?.map((Field, index) => {
const touchedField = Field.name as keyof typeof formik.touched;
const errorsField = Field.name as keyof typeof formik.errors;
switch (Field.fieldType) {
case 'input':
return <InputField Field={Field} formik={formik} touchedField={touchedField} errorsField={errorsField} key={Field.name}/>
case 'select':
return <SelectField Field={Field} formik={formik} key={Field.name}/>
case 'newLine':
return <div className={Field.classWrapper} key={index}></div>
}
})}
...
</form>
</>
)
}
Updating:
After adding 'enableReinitialize' (with help of #Sheraff), form show dates and works well, but console give me an error as 'A component is changing an uncontrolled input to be controlled...'. Then I added value property to input element to make it controlled. But now date picker doesnt update ui when I change date manuelly (it returns correct date as its value). If I enter value 'undefined' in formFieldList then it works again, but this time I coundt initialize my date dynamically.
How can I fix it?

AntDesign Cascader: error Not found value in options

I am wanting to use the "Cascader" component of "Ant Design" but I am having trouble filling it with data. This is my code, which I am doing wrong, sorry I am still a newbie and I need your support please.
function CascaderEmpCliUn(props) {
const optionLists = { a: []}
const [state, setState] = useState(optionLists);
useEffect(() => {
async function asyncFunction(){
const empresas = await props.loginReducer.data.empresas;
const options = [
empresas.map(empresa => ({
value: empresa.id,
label: empresa.siglas,
children: [
empresa.cli_perm.map(cliente => ({
value: cliente.id,
label: cliente.siglas,
children: [
cliente.uunn_perm.map(un => ({
value: un.id,
label: un.nombre,
}))
]
}))
]})
)
];
setState({a : options})
}
asyncFunction();
}, [])
return (
<Cascader options={state.a} placeholder="Please select" />
)
}
ERROR
Not found value in options
I was able to reproduce your error with dummy data whenever I had an empty array of children at any level. I'm not sure why this should be a problem, but it is. So you need to modify your mapping function to check the length of the child arrays. It seems to be fine if passing undefined instead of an empty array if there are no children.
General Suggestions
You don't need to store the options in component state when you are getting them from redux. It can just be a derived variable. You can use useMemo to prevent unnecessary recalculation.
You are passing the entire loginReducer state in your props which is not ideal because it could cause useless re-renders if values change that you aren't actually using. So you want to minimize the amount of data that you select from redux. Just select the empresas.
Revised Code
function CascaderEmpCliUn() {
// you could do this with connect instead
const empresas = useSelector(
(state) => state.loginReducer.data?.empresas || []
);
// mapping the data to options
const options = React.useMemo(() => {
return empresas.map((empresa) => ({
value: empresa.id,
label: empresa.siglas,
children:
empresa.cli_perm.length === 0
? undefined
: empresa.cli_perm.map((cliente) => ({
value: cliente.id,
label: cliente.siglas,
children:
cliente.uunn_perm.length === 0
? undefined
: cliente.uunn_perm.map((un) => ({
value: un.id,
label: un.nombre
}))
}))
}));
}, [empresas]);
return <Cascader options={options} placeholder="Please select" />;
}
The final code of "options" object:
const options = useMemo(() => {
return empresas.map((empresa) => ({
value: empresa.id,
label: empresa.siglas,
children:
empresa.cli_perm.length === 0
? console.log("undefined")
:
empresa.cli_perm.map((cliente) => ({
value: cliente.id,
label: cliente.siglas,
children:
cliente.uunn_perm.length === 0
? console.log("undefined")
:
cliente.uunn_perm.map((un) => ({
value: un.id,
label: un.nombre
}))
}))
}));
}, [empresas]);

React select set value in select

As I understood, in react-select (latest) you need to send an object to the value props, to select/preselect an option. I am also using react-hook-form for my form management.
Is it possible to set the value through react-select or do I need to use react-hook-form's setValue()
As I click on an Item, I send this to my states:
const handleSectionOnClick = (id) => {
setTravelRoute({
'LocationEnd': { value: +travelRoutes[id].LocationIdEnd, label: travelRoutes[id].LocationCityEnd },
'LocationStart': { value: +travelRoutes[id].LocationIdStart, label: travelRoutes[id].LocationCityStart },
'startDate': new Date(),
'endDate': new Date()
})
}
My Demo object looks like this:
{
'LocationCityEnd': "Berlin, Ger",
'LocationCityStart': "Hattingen, Ger",
'LocationIdEnd': 2,
'LocationIdStart': 1,
'startDate': new Date(),
'endDate': new Date()
}
My (LocationStart) select component looks like this:
<Controller
render={({ onChange }) => (
<Select
styles={customStyles}
onChange={handleOnChangeStartLocation}
name="startLocation"
value={travelRoute?.LocationStart}
options={selectOptions}
placeholder="Choose..."
/>
)}
name="startLocation"
className={`${errors.startLocation ? 'inputSelectError' : ''}`}
control={control}
rules={{ required: true }}
/>
Nothing gets selected, not even the value/label.
What Am I missing? Thank you!
EDIT:
I added the handleOnChangeStartLocation function:
const handleOnChangeStartLocation = e => {
const { value, label } = e;
setTravelRoute(prevState => ({
...prevState,
LocationIdStart: value,
LocationCityStart: label
}))
}
The problem seems to be that you are not updating LocationStart with the new value/label. You should do something like:
const handleOnChangeStartLocation = e => {
const { value, label } = e;
setTravelRoute(prevState => ({
...prevState,
LocationStart: e, // add this line of code
LocationIdStart: value,
LocationCityStart: label
}))
}

React Selecting dropdown option multiple times

I am wanting to be able to select a language I have called "Skip" more than one time without it disappearing from the dropdown. Currently, any language I select will disappear from the dropdown. Is this possible? Here's my code:
const languages = [
{ key: 'skip', text: 'Skip', value: 'Skip' },
{ key: 'english', text: 'English', value: 'English' },
{ key: 'french', text: 'French', value: 'French' },
]
handleAudioInputChange = (e, { name, value }) => this.setState( { [name]: value })
<Form.Select fluid search label='Audio Channels' name='audiochannelsValue' value={audiochannelsValue} options={languages} placeholder='Choose Audio Channels' onChange={this.handleAudioInputChange} multiple = "true"/>
I've tried multiple things such as hideSelectedOptions = {false}, but this does not seem to work. Any ideas?
If you only want a string based on the user input, you could do:
handleAudioInputChange = (e, { value }) => {
this.setState(prevState => {
const newVal = `${prevState.audiochannelsValue.length ? '/' : ''}${value}`;
return {
textValue: prevState.textValue.concat(newVal),
audiochannelsValue: value
};
});
}
This will build a string based on the previous state and separate each value with /. Haven't tested it, but it should generate (in order):
English
English/Skip
English/Skip/French
English/Skip/French/Skip

SetState of an array of Objects in React

Ok, so I'm so frustrated finding the right solution so I'm posting the problem here. Giving an answer would help me a lot, coz I'm stuck!
the state tree looks like this
this.state = {
itemList : [{
_id : 1234,
description : 'This the description',
amount : 100
}, {
_id : 1234,
description : 'This the description',
amount : 100
}],
}
The problems are :
can not update any specific key in the Object of the array according
to the _id
The previous state should remain intact
answered March 25 2018
This is how you would use setState and prevstate to update a certain attribute of an object in your data structure.
this.setState(prevState => ({
itemList: prevState.itemList.map(
obj => (obj._id === 1234 ? Object.assign(obj, { description: "New Description" }) : obj)
)
}));
answered Dec 12 2019 (REACT HOOKS)
import React, { useState } from 'react';
const App = () => {
const [data, setData] = useState([
{
username: '141451',
password: 'password',
favoriteFood: 'pizza',
},
{
username: '15151',
password: '91jf7jn38f8jn3',
favoriteFood: 'beans'
}
]);
return (
<div>
{data.map(user => {
return (
<div onClick={() => {
setData([...data].map(object => {
if(object.username === user.username) {
return {
...object,
favoriteFood: 'Potatos',
someNewRandomAttribute: 'X'
}
}
else return object;
}))
}}>
{JSON.stringify(user) + '\n'}
</div>
)
})}
</div>
)
}
to update state constructed like this you will have to find index of element you want to update, copy the array and change found index.
it's easier and more readable if you keep list of records as object, with id as a key and record as a value.
The only way to do this will be to copy itemList, modify it, and set the state to it.
update() {
let itemList = this.state.itemList.slice();
//update it
this.setState({ itemList });
}
Best way to update data into an array of objects
onChange={ (e) => this.setState({formData: { ...this.state.formData, 'plan_id': e.target.value}})}

Resources