I am passing through the react-select Select component as an InputComponent within the Material-UI InputBase component. I have successfully been able to populate the value from the options, however, I'm unable to use isClearable.
When isClearable is triggered, null is passed to the handleChange(event) function and I'm hoping there is a way to force an object through to prevent null creating an error.
The handleChange function within InputBase has var element = event.target || inputRef.current. As event is null, it's not even getting to inputRef which will contain the required object.
Would be good to get this working as an uncontrolled component.
I have created a codebox to illustrate the problem: https://codesandbox.io/s/morning-feather-l7xqf
You could supply your custom onChange() to catch the null and pass through your own value:
// Deconstruct from otherProps
const SelectWrapper = ({ inputRef, onChange, ...otherProps }) => {
function handleChange(event) {
// Overwrite the event with your own object if it doesn't exist
if (!event) {
event = {
target: inputRef,
value: '',
};
}
onChange(event);
}
return (
// Pass in the custom handleChange
<Select styles={customStyle} isClearable ref={inputRef} onChange={handleChange} {...otherProps} />
);
};
Related
I want to change the state whenever I delete an element from the react-select multi varient select option.
There is a second param in the onChange function for react-select, where it captures the context, meaning it has information about what action is performed or what element got removed.
const onChange = (value, context) => {
// console.log(value);
if (context.action === "remove-value") {
console.log(context.removedValue.id);
const newState = newitems.filter(
(item) => item.id !== context.removedValue.id
);
setnewitems(newState);
}};
<Select
isMulti
options={options}
onChange={onChange}
/>
I'm creating a form with a file upload with help of react-hook-form and Yup. I am trying to use the register method in my child component. When passing register as a prop (destructured in curly braces) the validation and submiting doesn't work. You can always submit the form and the submitted file object is empty.
Here's a sandbox link.
There are several of problems with your code.
1- register method returns an object with these properties:
{
onChange: function(){},
onBlur:function{},
ref: function(){}
}
when you define your input like this:
<input
{...register('photo')}
...
onChange={(event) => /*something*/}
/>
actually you are overrding the onChange method which has returned from register method and react-hook-form couldn't recognize the field change event. The solution to have your own onChange alongside with react-hook-form's onChange could be something like this:
const MyComp = ()=> {
const {onChange, ...registerParams} = register('photo');
...
return (
...
<input
{...params}
...
onChange={(event) => {
// do whatever you want
onChange(event)
}}
/>
);
}
2- When you delete the photo, you are just updating your local state, and you don't update photo field, so react-hook-form doesn't realize any change in your local state.
the problems in your ImageOne component could be solved by change it like this:
function ImageOne({ register, errors }) {
const [selectedImage, setSelectedImage] = useState(null);
const { onChange, ...params } = register("photo");
return (
...
<Button
className="delete"
onClick={() => {
setSelectedImage(null);
//Make react-hook-form aware of changing photo state
onChange({ target: { name: "photo", value: [] } });
}}
>
...
<input
//name="photo"
{...params}
type="file"
accept="image/*"
id="single"
onChange={(event) => {
setSelectedImage(event.target.files[0]);
onChange(event); // calling onChange returned from register
}}
/>
...
);
}
3- Since your input type is file so, the value of your photo field has length property that you can use it to handle your validation like this:
const schema = yup.object().shape({
photo: yup
.mixed()
.test("required", "photo is required", value => value.length > 0)
.test("fileSize", "File Size is too large", (value) => {
return value.length && value[0].size <= 5242880;
})
.test("fileType", "Unsupported File Format", (value) =>{
return value.length && ["image/jpeg", "image/png", "image/jpg"].includes(value[0].type)
}
)
});
Here is the full edited version of your file.
i'm entirely lost when it comes to figuring this out. I have a existing select that works with formik & react-select, but I can't get it to work as a multi. Here's what i have so far:
import Select from 'react-select';
import { useField } from 'formik';
export default function SelectField(props) {
const [field, state, { setValue, setTouched }] = useField(props.field.name);
const onChange = ({ value }) => {
setValue(value);
};
return <Select {...props} onChange={onChange} onBlur={setTouched} />;
}
and
<Field
component={SelectField}
name="campfeatures"
options={selectObjects}
/>
How would I turn this into being capable of taking multi select? If I add isMulti to the Field, it "works" but it doesn't actually retain the multiple values.
Thanks for your help!
The argument type for onChange changes when the react-select receives isMulti from a single object to a list of objects. When using isMulti you don't need to destruct; the first parameter is the value.
You also want to make the react-select a controlled component by managing its value.
export default function SelectField(props) {
const [field, state, { setValue, setTouched }] = useField(props.field.name);
// value is an array now
const onChange = (value) => {
setValue(value);
};
// use value to make this a controlled component
// now when the form receives a value for 'campfeatures' it will populate as expected
return <Select {...props} value={state?.value} isMulti onChange={onChange} onBlur={setTouched} />;
}
The new value is an array of the selected options with label and value fields. If you want to store just the value you'll need to map it to a value and modify the react-select to handle that
I am unable to figure out how I could get pre-populated value from disabled input field. Since the field is disabled onChange won't apply here. Even with inputRef property of TextField, I am able to trigger a function called getNameInputValue and can see the values via console. But when I try to set the state to a function coming via props, I get null errors for value.
Here's the relevant code snippet:
const getNameInputValue = (id, nameValue) => {
console.log(id, nameValue.value); //works
props.getNameValues({ ...nameValues, [id]: nameValue.value }) //doesnt work
}
return (
<Box>
<Typography>NAME</Typography>
<TextField
id={`name-${index}`}
disabled
defaultValue={nameValueComingFromTheLoop}
variant="outlined"
inputRef={(inputValue) => getNameInputValue(`name-${index}`,inputValue)}
/>
</Box>
);
As requested getNameValues is defined in an outer component, here is its definition:
const [nameValue, setNameValue] = useState({});
const getNameValues= (receivedNameValuesObj) => {
setNameValue(receivedNameValuesObj);
};
In vanilla js if we want to add a listener to a dom we can do like this
<div onclick="myFunction(this)">...</div>
but wen i do it inside react component this is refers to component class itself. how to handle something like this?
You should pass event in your function, and then you access DOM element with e.target.
For example if you want to handle input change, you can do something like this:
const [values, setValues] = useState({
username: '',
});
const handleChange = event => {
event.persist();
setValues(values => ({
...values,
[event.target.id]: event.target.value
}));
};
event.target.id is the id of DOM element and event.target.value is the value of the same element, which in this case is input.
<input
id="username"
type="text"
placeholder="Enter username"
onChange={handleChange}
value={values.username}
required
></input>
Also
Note:
If you want to access the event properties in an asynchronous way, you should call event.persist() on the event, which will remove the synthetic event from the pool and allow references to the event to be retained by user code.
In react u should use onClick (with capital C) and pass the event to your function
<div onClick={(e) => {myfunction(e)}}>...</div>
Then in your function, use event.target to get the clicked tag.
const myfunction = (event) => {
let id = event.target.id;
}
In your context, this refers to the DOM target element, so you'd define your function as:
function myFunction(target) { /* Code here */ }
But in react, when you define an input:
<input onClick={myFunction} />
Your function is given an event not a target, so your function is defined as:
function myFunction(event) { console.log(event.target) }