Using React Hook Form, when I want to collect data by sending register as props to child component to take input value from child component, it shows 'register is not a function' error.
How can I solve this?
const { register, formState: { errors }, handleSubmit } = useForm();
const onSubmit = (data) => console.log(data);
<form onSubmit={handleSubmit(onSubmit)}>
<fieldset>
<legend className='text-[#666666]' >Status</legend>
{
statusData.map(status => <CheckboxFilter register={register} key={status._id} status={status}/>)
}
</fieldset>
</form>
here child
//CheckboxFilter component
const CheckboxFilter = ({ status, register }) => {
return (
<>
<p className='text-[#858585] mt-2 text-[14px]' >
<label htmlFor={status?.name} className='cursor-pointer' >
<input {...register("checkBoxData")} type="checkbox" name="status" id={status?.name} value={"status?.name"} /> {status?.name}
</label>
</p>
</>
);
};
I created a sandbox here codesandbox and it works perfectly.
I took your code and only changed the CheckboxFilter component:
Removed the name property (register function returns the name of the input based in the string you pass to it as a parameter, you should not override it)
Removed the value property (that was making the value of the checkbox constant, because there wasn't onChange handler that was modifying it)
Changed ...register("checkBoxData") to ...register(checkBoxData${name}) so this way you can have every checkbox value individually in the form.
Anyway, if you want to have a different behaviour than what I have assumed, let me know and I will help.
-Ado
I have a radio button group in a component. I want to set which radio is selected based on it's value taken from the database in an Update/Edit scenario.
export default function updateRadioSelection(){
const [radioValue, setRadiovalue] = useState("");
useState(()=>{
setRadiovalue("pick"); // <-- Assume this value is taken from database, value may be either "delivery" or "pick"
}, [])
const changeSelection = (e)=>{
setRadiovalue(e.target.value);
}
return(
<div>
<input type="radio" id="delivery" name="orderType" value="delivery" required onChange={changeSelection} />
<label htmlFor="delivery">Delivery</label>
<input type="radio" id="pick" name="orderType" value="pick" onChange={changeSelection} />
<label htmlFor="pick">Pick Up</label>
</div>
)
}
To make a checkbox or radio checked you must use the checked prop for the input element, it receives a boolean value. And you can do something like this
export default function updateRadioSelection(){
const [radioValue, setRadiovalue] = useState("");
// useState will not execute any kind of callback function, for this case you need to use useEffect
useEffect(() => {
const dbResult = getRadioFromDb();
setRadiovalue(dbResult);
}, [])
const changeSelection = (e)=>{
setRadiovalue(e.target.value);
}
return(
<div>
<input type="radio" id="delivery" name="orderType" value="delivery" required onChange={changeSelection} checked={radioValue === 'delivery'} />
<label htmlFor="delivery">Delivery</label>
<input type="radio" id="pick" name="orderType" value="pick" onChange={changeSelection} checked={radioValue === 'pick'} />
<label htmlFor="pick">Pick Up</label>
</div>
)
}
You can read more about radio input in its documentation
Just a few minutes after posting this question I found the answer I was searching for. Turns out it's pretty easy.
Just add checked={radioValue === "pick"} for the Pick Up radio button & the same for other radio button by replacing "pick" with "delivery"
reference - react.tips/radio-buttons-in-reactjs
what i am trying to achieve that i made a react custom component for an input field looks like this in a seperate folder with name input.js
export function Input({id,type,label,name}) {
return (
<div className="form_container">
<input type={type} id={id} className="form__input" name={name} autoComplete="" placeholder=" " />
<label htmlFor="email" className="form__label">{label}</label>
</div>
)
}
and i used it inside my contact.js file which i want to use multiple times like this
const fname = useRef(null);
<form action="" onSubmit={sendEmail}>
<Input ref={fname} id='inputName' type="text" name="fname" label="Name" />
<button type='submit'> Submit </button>
</form>
const sendEmail = (e) => {
e.preventDefault();
console.log(fname.current.value);
}
but i am getting this error while trying to refresh the page and i cannot access the input to get the value of it
react-dom.development.js:67 Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
only the old class components can be given refs natively.
use need useImperativeHandle hook combined with forwardRef
https://reactjs.org/docs/hooks-reference.html#useimperativehandle
I am unable to clear the value of a material-ui Input using refs, not state.
I've tried both types of refs that I know about:
ref={this.input}
- and -
ref={el => (this.input = el)}
but neither seems to work w/ a material-ui Input
the following similar questions did not help:
How to get input value of TextField from Material UI?
Clear and reset form input fields
Clear an input field with Reactjs?
how to set Input value in formField ReactJs
Here's a snippet of my React JSX for the input & button:
<Input
type="text"
id="name"
inputComponent="input"
ref={el => (this.name = el)}
/>
<Button
variant="contained"
onClick={this.handleClear}
className="materialBtn"
>
Clear
</Button>
And the event handler that I expect should clear the input value:
handleClear() {
this.name.value = "";
}
I can make the code work fine using a standard HTML5 input, but not with a material-ui input, which is a requirement of this project. Additionally, this element's value is NOT in react state and I am not looking for a solution that requires using state -- I need to keep this piece as an uncontrolled component.
What am I missing w/ regard to material-ui? I have combed their docs/api but haven't found anything that suggests it needs to be handled differently from a standard input. thanks
Here's an example on CodeSandbox showing the failure w/ a material-ui input and success w/ an HTML5 input:
https://codesandbox.io/s/fancy-frost-joe03
I figured it out, you are using the wrong prop for the ref.
You should be using inputRef prop.
Here is the correct version,
<Input
type="text"
id="name"
inputComponent="input"
inputRef={el => this.name = el}
/>
<Button
variant="contained"
onClick={this.handleClear}
className="materialBtn"
>
Clear
</Button>
handleClear() {
this.name.value = "";
}
The reason is that the Material Input component creates an element with the following structure,
<div class="MuiInputBase-root MuiInput-root MuiInput-underline">
<input class="MuiInputBase-input MuiInput-input" id="name" type="text" value=""></input>
</div>
So, using ref would reference the root element which is <div>. So, they created a separate prop called inputRef to reference the child element <input>.
I updated your codesandbox.io code and saved it. Check out the full working code here,
https://codesandbox.io/s/elastic-dhawan-l4dtf
import React, { useState } from 'react'
import { Button, Container, InputBase } from '#material-ui/core'
const ClearText = ()=> {
const [text , setText] = useState("")
}
const clearTextField = () => setText("")
return (
<Container>
<InputBase
value={text ? text : ""}
onChange={(e)=>setText(e.target.value)}
/>
<Button onClick={clearTextField} > Clear </Button>
</Container>
)
};
export default ClearText;
I'm trying to create a simple form with react, but facing difficulty having the data properly bind to the defaultValue of the form.
The behavior I'm looking for is this:
When I open my page, the Text input field should be filled in with the text of my AwayMessage in my database. That is "Sample Text"
Ideally I want to have a placeholder in the Text input field if the AwayMessage in my database has no text.
However, right now, I'm finding that the Text input field is blank every time I refresh the page. (Though what I type into the input does save properly and persist.) I think this is because the input text field's html loads when the AwayMessage is an empty object, but doesn't refresh when the awayMessage loads. Also, I'm unable to specify a default value for the field.
I removed some of the code for clarity (i.e. onToggleChange)
window.Pages ||= {}
Pages.AwayMessages = React.createClass
getInitialState: ->
App.API.fetchAwayMessage (data) =>
#setState awayMessage:data.away_message
{awayMessage: {}}
onTextChange: (event) ->
console.log "VALUE", event.target.value
onSubmit: (e) ->
window.a = #
e.preventDefault()
awayMessage = {}
awayMessage["master_toggle"]=#refs["master_toggle"].getDOMNode().checked
console.log "value of text", #refs["text"].getDOMNode().value
awayMessage["text"]=#refs["text"].getDOMNode().value
#awayMessage(awayMessage)
awayMessage: (awayMessage)->
console.log "I'm saving", awayMessage
App.API.saveAwayMessage awayMessage, (data) =>
if data.status == 'ok'
App.modal.closeModal()
notificationActions.notify("Away Message saved.")
#setState awayMessage:awayMessage
render: ->
console.log "AWAY_MESSAGE", this.state.awayMessage
awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else "Placeholder Text"
`<div className="away-messages">
<div className="header">
<h4>Away Messages</h4>
</div>
<div className="content">
<div className="input-group">
<label for="master_toggle">On?</label>
<input ref="master_toggle" type="checkbox" onChange={this.onToggleChange} defaultChecked={this.state.awayMessage.master_toggle} />
</div>
<div className="input-group">
<label for="text">Text</label>
<input ref="text" onChange={this.onTextChange} defaultValue={awayMessageText} />
</div>
</div>
<div className="footer">
<button className="button2" onClick={this.close}>Close</button>
<button className="button1" onClick={this.onSubmit}>Save</button>
</div>
</div>
my console.log for AwayMessage shows the following:
AWAY_MESSAGE Object {}
AWAY_MESSAGE Object {id: 1, company_id: 1, text: "Sample Text", master_toggle: false}
Another way of fixing this is by changing the key of the input.
<input ref="text" key={this.state.awayMessage ? 'notLoadedYet' : 'loaded'} onChange={this.onTextChange} defaultValue={awayMessageText} />
Update:
Since this get upvotes, I will have to say that you should properly have a disabled or readonly prop while the content is loading, so you don't decrease the ux experience.
And yea, it is most likely a hack, but it gets the job done.. ;-)
defaultValue is only for the initial load
If you want to initialize the input then you should use defaultValue, but if you want to use state to change the value then you need to use value. Personally I like to just use defaultValue if I'm just initializing it and then just use refs to get the value when I submit. There's more info on refs and inputs on the react docs, https://facebook.github.io/react/docs/forms.html and https://facebook.github.io/react/docs/working-with-the-browser.html.
Here's how I would rewrite your input:
awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else ''
<input ref="text" onChange={this.onTextChange} placeholder="Placeholder Text" value={#state.awayMessageText} />
Also you don't want to pass placeholder text like you did because that will actually set the value to 'placeholder text'. You do still need to pass a blank value into the input because undefined and nil turns value into defaultValue essentially. https://facebook.github.io/react/tips/controlled-input-null-value.html.
getInitialState can't make api calls
You need to make api calls after getInitialState is run. For your case I would do it in componentDidMount. Follow this example, https://facebook.github.io/react/tips/initial-ajax.html.
I'd also recommend reading up on the component lifecycle with react. https://facebook.github.io/react/docs/component-specs.html.
Rewrite with modifications and loading state
Personally I don't like to do the whole if else then logic in the render and prefer to use 'loading' in my state and render a font awesome spinner before the form loads, http://fortawesome.github.io/Font-Awesome/examples/. Here's a rewrite to show you what I mean. If I messed up the ticks for cjsx, it's because I normally just use coffeescript like this, .
window.Pages ||= {}
Pages.AwayMessages = React.createClass
getInitialState: ->
{ loading: true, awayMessage: {} }
componentDidMount: ->
App.API.fetchAwayMessage (data) =>
#setState awayMessage:data.away_message, loading: false
onToggleCheckbox: (event)->
#state.awayMessage.master_toggle = event.target.checked
#setState(awayMessage: #state.awayMessage)
onTextChange: (event) ->
#state.awayMessage.text = event.target.value
#setState(awayMessage: #state.awayMessage)
onSubmit: (e) ->
# Not sure what this is for. I'd be careful using globals like this
window.a = #
#submitAwayMessage(#state.awayMessage)
submitAwayMessage: (awayMessage)->
console.log "I'm saving", awayMessage
App.API.saveAwayMessage awayMessage, (data) =>
if data.status == 'ok'
App.modal.closeModal()
notificationActions.notify("Away Message saved.")
#setState awayMessage:awayMessage
render: ->
if this.state.loading
`<i className="fa fa-spinner fa-spin"></i>`
else
`<div className="away-messages">
<div className="header">
<h4>Away Messages</h4>
</div>
<div className="content">
<div className="input-group">
<label for="master_toggle">On?</label>
<input type="checkbox" onChange={this.onToggleCheckbox} checked={this.state.awayMessage.master_toggle} />
</div>
<div className="input-group">
<label for="text">Text</label>
<input ref="text" onChange={this.onTextChange} value={this.state.awayMessage.text} />
</div>
</div>
<div className="footer">
<button className="button2" onClick={this.close}>Close</button>
<button className="button1" onClick={this.onSubmit}>Save</button>
</div>
</div>
That should about cover it. Now that is one way to go about forms which uses state and value. You can also just use defaultValue instead of value and then use refs to get the values when you submit. If you go that route I would recommend you have an outer shell component (usually referred to as high order components) to fetch the data and then pass it to the form as props.
Overall I'd recommend reading the react docs all the way through and do some tutorials. There's lots of blogs out there and http://www.egghead.io had some good tutorials. I have some stuff on my site as well, http://www.openmindedinnovations.com.
it's extremely simple, make defaultValue and key the same:
<input defaultValue={myVal} key={myVal}/>
This is one of the recommended approaches at https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key
To force the defaultValue to re-render all you need to do is change the key value of the input itself. here is how you do it.
<input
type="text"
key={myDynamicKey}
defaultValue={myDynamicDefaultValue}
placeholder="It works"/>
Maybe not the best solution, but I'd make a component like below so I can reuse it everywhere in my code. I wish it was already in react by default.
<MagicInput type="text" binding={[this, 'awayMessage.text']} />
The component may look like:
window.MagicInput = React.createClass
onChange: (e) ->
state = #props.binding[0].state
changeByArray state, #path(), e.target.value
#props.binding[0].setState state
path: ->
#props.binding[1].split('.')
getValue: ->
value = #props.binding[0].state
path = #path()
i = 0
while i < path.length
value = value[path[i]]
i++
value
render: ->
type = if #props.type then #props.type else 'input'
parent_state = #props.binding[0]
`<input
type={type}
onChange={this.onChange}
value={this.getValue()}
/>`
Where change by array is a function accessing hash by a path expressed by an array
changeByArray = (hash, array, newValue, idx) ->
idx = if _.isUndefined(idx) then 0 else idx
if idx == array.length - 1
hash[array[idx]] = newValue
else
changeByArray hash[array[idx]], array, newValue, ++idx
Related issue
Setting defaulValue on control din't not update the state.
Doing reverse works perfectly:
Set state to default value, and the control UI gets updated correctly as if defaulValue was given.
Code:
let defaultRole = "Owner";
const [role, setRole] = useState(defaultRole);
useEffect(() => {
setMsg(role);
});
const handleChange = (event) => {
setRole(event.target.value );
};
// ----
<TextField
label="Enter Role"
onChange={handleChange}
autoFocus
value={role}
/>
Define a state for your default value
Surround your input with a div and a key prop
Set the key value to the same value as the defaultValue of the input.
Call your setDefaultValue defined at the step 1 somewhere to re-render your component
Example:
const [defaultValue, setDefaultValue] = useState(initialValue);
useEffect(() => {
setDefaultValue(initialValue);
}, false)
return (
<div key={defaultValue}>
<input defaultValue={defaultValue} />
</div>
)
Give value to parameter "placeHolder".
For example :-
<input
type="text"
placeHolder="Search product name."
style={{border:'1px solid #c5c5c5', padding:font*0.005,cursor:'text'}}
value={this.state.productSearchText}
onChange={this.handleChangeProductSearchText}
/>
Use value instead of defaultValue and change the value of the input with the onChange method.