I use redux-form for my form, following the example: https://redux-form.com/8.3.0/docs/api/fields.md/
So the <Fields /> is like so:
<Fields
names={['firstName', 'lastName']}
component={input}
validate={{
firstName: (value, allValues, props, name) => 'error'
}}
warn={{
lastName: (value, allValues, props) => 'warning'
}}
/>
the fields component that i render is this
const renderFields = (fields) => (
<div>
<div className="input-row">
<input {...fields.firstName.input} type="text"/>
{fields.firstName.meta.touched && fields.firstName.meta.error &&
<span className="error">{fields.firstName.meta.error}</span>}
</div>
<div className="input-row">
<input {...fields.lastName.input} type="text"/>
{fields.lastName.meta.touched && fields.lastName.meta.error &&
<span className="error">{fields.lastName.meta.error}</span>}
</div>
</div>
)
So far so good, the form displays the 2 input fields and i can add values into them.
But how do i pass default values into the input's ?
When i add the value property into the input, i cant edit the input afterwards.
For example, i add the value prop with a value like so:
const renderFields = (fields) => (
<div>
<div className="input-row">
// add value="my name"
<input {...fields.firstName.input} type="text" value="my name" />
{fields.firstName.meta.touched && fields.firstName.meta.error &&
<span className="error">{fields.firstName.meta.error}</span>}
</div>
<div className="input-row">
// add value="my last name"
<input {...fields.lastName.input} type="text" value="my last name" />
{fields.lastName.meta.touched && fields.lastName.meta.error &&
<span className="error">{fields.lastName.meta.error}</span>}
</div>
</div>
)
In that way, the inputs have always the same init value.
Any help on how to have default value and also be able to edit it, thank you.
When you provide the value prop you will need to provide onChange function as well and handle the state - https://reactjs.org/docs/forms.html#controlled-components
and from redux-form docs: https://redux-form.com/8.3.0/docs/api/field.md/#-code-onchange-event-newvalue-previousvalue-name-gt-void-code-optional-
You need a state variable to hold the input value.
const [inputValue, setInputValue] = useState('');
In the input tag, use the previously declared state variable as value & in onChange of input, set the input value to the target value.
<input type="text" value={inputValue} onChange={e => setInputValue(e.target.value)} />
You can use prop defaultValue, as mentioned in Redux Form documentation :https://redux-form.com/6.0.0-alpha.4/docs/api/field.md/#props
<Field component={your custom component} defaultValue={}/>
Related
An example:
function App() {
const [user, setUser] = React.useState({
name: '',
surname: ''
});
const handleChange = e => {
setUser(prev => ({...prev, [e.target.name]: e.target.value}));
};
return (
<div className="App">
<form onChange={handleChange}>
<input type="text" name="name" placeholder="name" value={user.name} />
<input type="text" name="surname" placeholder="surname" value={user.surname} />
</form>
</div>
);
}
ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div class='react'></div>
And I get these warnings:
Warning: You provided a value prop to a form field without an
onChange handler. This will render a read-only field. If the field
should be mutable use defaultValue. Otherwise, set either onChange
or readOnly.
Should I just ignore them?
Technically, there's nothing wrong about your code. Then yes, you can safely ignore the logs.
However, event delegation is not the "React way" to deal with form states, so you won't be able to get rid of those warnings.
IMHO it's not a good development experience so you could just add a onChange={handleChange} to each field rather than setting the onChange on the form. I guess that the React event system is optimized enough to not have to do event delegation, even if in your use case it was pretty convenient and elegant.
Your OnChange needs to be in the inputs and not in the form, here is an example:
<input type="text" name="name" placeholder="name" value={user.name} onChange={handleChangeName}/>
<input type="text" name="surname" placeholder="surname" value={user.surname} onChange={handleChangeSurName}/>
and this is the handle change of name
const handleChangeName = (e) => {
setUser(userInfo => {...userInfo, ['name']: e.target.value})
}
Because you don't have onChange in your inputs, it generates this alert.
I have a form with a single input which is query for the search url -
<form className={styles.searchInputContainer} role="search" action="/search">
<label htmlFor={id} className={styles.searchLabel}>{constants.search}</label>
<input id={id} className={styles.searchInput} type="search" name="q" placeholder="Search..." autocomplete="off" />
</form>
At the moment, when I hit enter, the url will end with '/search?q=whatever_the_input_is'
I am currently doing logic elsewhere in the file to check if the search bar is on a particular page. With this boolean value (isShoppingPage), I want to optionally append a 'type=shop' to the url when the user hits enter so it automatically only searches through the shop categories. I've tried appending 'type=shop' to the input value but then the url ends up having URL encoding. I'm confused as to how to conditionally add this without changing the name="q" which the input currently has and needs.
Instead of action you can use onSubmit prop of the form.
import React, {useState} from 'react';
const Form = (props) => {
const {styles, id, constants} = props;
const [qValue, setQValue] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
// here you can make API call with q=qValue and type=shop
};
return (
<form className={styles.searchInputContainer} role="search" onSubmit={handleSubmit}>
<label htmlFor={id} className={styles.searchLabel}>{constants.search}</label>
<input id={id} className={styles.searchInput} type="search" name="q" placeholder="Search..." autocomplete="off" value={qValue} onChange={e => setQValue(e.target.value)} />
</form>
)
};
This is guide from react docs
please , how can i get over this error of component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.
this prevent the input field from showing what i am typing.
const [sellerName, setSellerName] = useState();
const [storeName, setStoreName] = useState("");
...
<form className="registerInputContainer customerEditInput">
<div className="regInput passForm ">
<span className="userEmail">Owner's Full name (Required)</span>
<div className="passwordContainer editCusInputField">
<input
placeholder="your name"
value={sellerName }
onChange={(e) => setSellerName(e.target.value)}
className="passwordInput regInputField"
/>
</div>
</div>
<div className="regInput passForm ">
<span className="userEmail">Store Name (required)</span>
<div className="passwordContainer editCusInputField">
<input
placeholder=""
required
value={storeName || ""}
onChange={(e) => setStoreName(e.target.value)}
className="passwordInput regInputField"
/>
</div>
</div>
...
</form>
i have tried some of the solution i saw on stackoverflow but they are not working . thanks you all
I have some tags that display text they conditionally render <input /> tags by checking whether the edit state is true or false. When true, instead of showing text, I render an <input /> tag to make inline edit.
Everything works well. The only problem is, when one <button> tag changes the edit state to true, then, instead of showing input field for editing where Edit was clicked, every tag renders their input field.
How do I limit this rendering of input field for only those tags from where the edit state was changed by the Edit button click?
My code:
const [ edit, setEdit ] = useState(false);
const isEdit = edit;
<div>
<p>{ !isEdit ? (<span>Email: {userProfile.email} <button onClick={e=>setEdit(!edit)}>Edit</button></span>) : (<span>Email:
<input type="text" placeholder="email"
name="email" onChange={e=>setEmail(e.target.value)}/>
<button type="submit" onClick={addUserEmail}>Save</button></span>
)}
</p>
<p>About: { !isEdit ? (<span> {userProfile.about} <button onClick={e=>setEdit(!edit)}>Edit</button>
</span>) :
(<span>
<input type="text" placeholder="about"
name="about" onChange={e=>setAbout(e.target.value)}
/>
<button type="submit" onClick={addUserAbout}>Save</button>
</span>)
)}
</p>
</div>
There are a couple of solutions, but the cleanest way would probably be to separate those editable fields into their own component since each of them has its own state.
For example, you can create a generic EditableField component similar to this one:
function EditableComponent({defaultIsEditing = false, renderText, renderInput}) {
const [ isEditing, setIsEditing ] = useState(defaultIsEditing);
if(!isEditing){
//Non-edit mode
return (<span> {renderText()} <button onClick={e=>setEdit(!edit)}>Edit</button></span>);
}
//Edit mode
return renderInput();
}
Then use it as this:
<div>
<EditableComponent
renderText={() => <>Email: {userProfile.email}</>}
renderInput={() => (<span>Email:
<input type="text" placeholder="email" name="email" onChange={e=>setEmail(e.target.value)}/>
<button type="submit" onClick={addUserEmail}>Save</button>
</span>)}
/>
{/* ...repeat for all fields */}
</div>
This solution ensures that you don't repeat the same logic over and over. With two fields you might be okay just making two state variables (e.g. isEdit1, isEdit2), but the more you add the more cumbersome it will become.
Another alternative would be to store the name of the input you're editing as state, this will ensure that only one field is edited at a time, but you need to take care of saving old fields when starting to edit new ones
I struggling and a bit lost with react about one thing.
I have a row with 5 columns.
Each columns have a checkbox, an input and a button.
So if you look at it in a standard way, it looks like this:
render() {
return (
<div className="sbm-sources">
<div className="col-md-2 sbm-source">
<input type="checkbox" name="c1"/>Add to source.
<br />
<input type="text" name="i1" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</div>
<div className="col-md-2 sbm-source">
<input type="checkbox" name="c2"/>Add source to blipp.
<br />
<input type="text" name="i2" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</button>
</div>
<div className="col-md-2 sbm-source">
<input type="checkbox" name="c3" />Add source to blipp.
<br />
<input type="text" name="i3" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</button>
</div>
<div className="col-md-2 sbm-source">
<input type="checkbox" name="c4" />Add source to blipp.
<br />
<input type="text" name="i4" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</div>
<div className="col-md-2 sbm-source">
<input type="checkbox" name='c5' />Add source to blipp.
<br />
<input type="text" name="i5" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</div>
</div>
);
}
};
The thing is, each column can be validated separately but I need to know which one is trigger.
I know how to do it using the name of each one, but I am not sure that creating a state for EACH input / checkbox and then check which one is triggered one for then associating the data before sending a POST request is the best option here.
ex:
handleChecked(e){
if (e.value.name === c1){
this.setState({checkedC1: true});
}
...
}
This would quickly become messy and hard to maintain, or to make adaptable.
The thing is, when I want to do my Post request, I would love to receive an int. For example, if the checkbox (and / or) input filled is from the first column, the int received would be 0.
Is there ean elegant way of doing so with react? What would you suggest?
I am too much into my code and my lack of experience make me blind about it.
Many thanks!
You would need to keep the state of all your columns inside the parent component, because from there you send your post request.
create an array of column data, and put the array in state
inside render, use .map() to loop over the array and render a Column for each item in the array
optional: put the column inside a separate (stateless) component.
Your state could be like:
// as part of your constructor
let initialColumns = [
{ checked: false, checkText: "Add to source.", inputPh="Enter your query.", value: ""},
...
{ checked: false, checkText: "Add source to blipp.", inputPh="Enter your query.", value: ""}
];
this.state = { colDataArr: initialColumns }
And in your render do:
render() {
let expandInput = this.expandInput;
<div>
{this.state.colDataArr.map( colItem, index => {
<Column
checked = {colItem.checked}
checkText = {colItem.checkText}
...
expandInput = {(e) => { expandInput(e) }} // <== special here
colID = {index} // and here
})}
</div>
}
Create a (stateless) <Column> component that takes the function expandInput as a prop, alongside the other variable props. Whenever this function is called, you get the event, but also the index of the column (from 0-4).
That way, inside expandInput, you can handle one individual update
expandInput(event, type, index) {
// create copy of column object, to avoid mutating state directly
let origColData = this.state.colDataArr[index]
let newColData = {
checked = origColData.checked,
checktext = origColData.checkText,
...
}
// now, do whatever you need with the event data to update the newColData
if (type == "checkbox") {
let checked = e.target.checked
} else {
...
}
// copy the array and replace the updated one
let newColArr = this.state.colDataArr.slice()
newColArr[index] = newColData
// set the new state
this.setState({ colDataArr : newColArr })
}
UPDATE
And your shiny new stateless component could look something like this:
class Column extends React.Component {
constructor(props) {
super(props)
}
render() {
<div className="col-md-2 sbm-source">
<input type="checkbox"
onClick={(e) => this.props.expandInput(e,"checkbox", this.props.colID)}/>
{this.props.checkText}
<br />
<input type="text" size="10"
onChange={(e) => this.props.expandInput(e,"text", this.props.colID)}
placeholder={this.props.inputPH} />
<button
onClick={(e) => this.props.expandInput(e,"button", this.props.colID)}>
Do something
</button>
</div>
}
}
Slightly easier way to maintain is to store the exact field variable name in the name instead, and do:
handleChecked(e){
this.setState({[e.value.name]: true});
...
}