How to pass data to a form in ReactJS? - javascript

How do you pass data to a form to edit in ReactJS?
I have 3 components, Table, Form and a parent component.
return (
<Table />
<Form />
);
Table renders a list of items and each row has an Edit button
So, when the Edit button is clicked, it will call an edit function (passed from parent using props) with the id of the item.
this.props.edit('iAmTheIdOfTheItem');
The edit function will set the id in the parent component state.
edit = (id) => {
this.setState({ id })
}
The selected item is passed to the Form component.
<Form item={ items.find(item => item.id === this.state.id) } />
This should store the current passed data in the Form component state. So that I can use that data to make any changes.
When the update button will be clicked, it will pass the state to the parent component.
Possible Solutions
componentDidMount
I can't set the state using componentDidMount since the Form component is already mounted.
id && < Form />
While this can help me use componentDidMount. But the problem is that the Form component is wrapped in a Modal component. So, closing animation will not work when I update the value and clear the id in the state.
getDerivedStateFromProps
I can use this one as a last resort but it looks like a hack to me and I'm not sure if I really need this. Since I've more than 5 forms, adding this every form does not look good to me.
Any better approach? Suggestions?
This should be helpful
https://codesandbox.io/s/82v98o21wj?fontsize=14

You can use Refs for this.
Reference the child using this.child = React.createRef() and pass this to your child component like this ref={this.child}. Now you can use that child's functions like this this.child.current.yourChildsFunction("object or data you want to pass") then on that child function, just set your form state using the data passed.
Here's a complete example. Sandbox HERE
class Parent extends Component {
constructor(props) {
super(props);
this.child = React.createRef();
}
onClick = () => {
this.child.current.fillForm("Jackie Chan");
};
render() {
return (
<div>
<button onClick={this.onClick}>Click</button>
<br />
<br />
<Child ref={this.child} />
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.state = {
name: ""
};
}
handleChange = ({ target }) => {
this.setState({ [target.name]: target.value });
};
fillForm(passedvalue) {
this.setState({ name: passedvalue });
}
render() {
return (
<form>
<label for="name">Name</label>
<input
id="name"
name="name"
onChange={this.handleChange.bind(this)}
value={this.state.name}
/>
</form>
);
}
}

Here an updated version of your sandbox: https://codesandbox.io/s/jprz3449p9
The process is quite basic:
You send the edited item to your form (via props)
The form stores a copy in its own state
When you change the form, it changes the local form state
On submit, you send the state to your parent with a callback (onUpdate here)
What you were missing:
Refresh the local form state when props change
if (!item || id !== item.id) {
this.setState(item);
}
Notify the parent when the item is updated with a callback
update = event => {
event.preventDefault();
this.props.onUpdate(this.state);
this.setState(emptyItem);
};

Related

How can I setState() another input value with a button in the same component in React?

How can I setState() another input value with a button in the same component in React?
I'm using the onClick event handler on the button.
I want to make the handleClickfunction which I gave it to the button, to target the value of the input
class Search extends Component {
state = {
searchInput: "",
};
handleClick = () => {
this.setState({
searchInput: input.value,
});
};
render() {
return (
<div>
<input type="text"/>
<button onClick={this.handleClick}>Enter</button>
</div>
);
}
}
Your question is not clear, I believe you are asking how to set the value of an input field when you press a button in react.
If that is correct, then you have done most of the work already, all you need to do now is add an <input> tag.
Like this:
<input type="text" value={ this.state.searchInput } />
If I have misunderstood your question then please clarify.
It may be worth reading about how State and Lifecycle work in React Here
Whenever the setState() function is triggered, React automatically runs the render() function in any components where state has changed, rerendering that component with the new state values.
Edit
After clarification I now understand exactly what you want.
You require the use of a ref, like this:
class Search extends Component {
state = {
searchInput: "",
};
handleClick = () => {
this.setState({
searchInput: this.inputText,
});
};
render() {
return (
<div>
<input type="text" ref={(x) => this.inputText = x}/>
<button onClick={this.handleClick}>Enter</button>
</div>
);
}
}
instead of using a button to update the state try this:
<input type="text" onChange={(e) => this.setState({searchInput: e.target.value }) />

React JS validate form

Hello friends I'm pretty new to React JS so I want to ask you this:
I have a Parent and Child component. In Child component I have inputs (name,email etc) and in Parent component I have Button to save the input data in database.
My question is: How to validate inputs(i want them to be required) so the button can NOT call saveData function(to save in database) if the inputs are empty.
Here is Parent Component:
class Parent extends Component{
saveData = (e) => {
//some code
}
render() {
return (
<div>
<Child/>
<Button color="primary" onClick={this.saveData}>Submit</Button>
</div>
);
}
}
And here is Child Component:
class Child extends React.Component {
onInputChange = (e) => {
e.preventDefault();
this.props.onInputChange(e.target.name, e.target.value);
};
render() {
return (
<FormGroup>
<Input name="email" onChange={this.onInputChange}/>
</FormGroup>
);
}
}
I cant see the implementation of your onInputChange function but looks like its coming from parent component.
So, if you have onInputChange in your parent component then, you can have a disable state that is passed to your Button component and you need to set that disable state when onInputChange is called i.e
onInputChange = (name, value) => {
if(value === ''){
this.setState({disable: true});
}
else{
this.setState({
name: value;
disable:false
})
}
}
And pass it to your button i.e.
<Child />
<Button color="primary" disabled={disable} onClick={this.saveData}>

Redux - How to get form values in its submit handler and call event.preventDefault()?

What I've been trying is to pass values from a form with Redux-Form to its handleSubmit with event.preventDefault(), so that submitting this form won't change URL.
Chances are that this is to do with react-router.
Here are excerpts of my attempt...
//form
class SampleForm extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<form onSubmit={this.props.handleSubmit}>
<Field name="body" component="textarea" />
<button type="submit" className="btn">Save</button>
</form>
</div>
);
}
}
//container...
class Container extends React.Component {
handleSubmit(event) {
event.preventDefault();
//How can I get the values from SampleForm?
}
render() {
return (
<div>
<SampleForm handleSubmit={this.handleSubmit}/>
</div>
);
}
}
I'm using react-router. Because of this, when I submit the form, the URL gets updated with the submitted values.
(When I was NOT using react-router, this didn't happen - I could simply get the values from handleSubmit().)
Any advice will be appreciated.
So you have your SampleForm component wrapped with Redux-Form. This decorated component should be passed an onSubmit prop from your Container which is the handler. You are passing handleSubmit() as a prop which is conflicting with redux-form's handleSubmit "middleware" that it is trying to do for you.
Change your Container component to pass "onSubmit" in in place of "handleSubmit" as follows:
//container...
class Container extends React.Component {
handleSubmit(event) {
event.preventDefault();
//How can I get the values from SampleForm?
}
render() {
return (
<div>
<SampleForm onSubmit={this.handleSubmit}/>
</div>
);
}
}
Now, your Container component should correctly receive the argument "values" as an argument and should be an object of form fields key/values. There is no need to call event.preventDefault() because redux-form's middleware (as I mentioned earlier) does that for you.
So instead of handleSubmit(event), change the event argument to "values" as below:
handleSubmit(values) {
console.log(values);
}

ReactJS: Maintain data state between parent and child

There's a parent, <MessageBox /> element, which contains a list of messages stored in its state. For each message in messages, a <Message /> element is created inside <MessageBox /> which has fields for message.subject and message.body. A user can edit the message.subject and message.body and once done, the message object is sent back to <MessageBox /> through a props.updateHandler() to maintain the message state in the parent.
In my current approach, I'm storing the message data in MessageBox's state and in the render() function, I'm creating the <Message /> elements and passing a callback to each of them to send back data changes. In the callback, the updated data from each of the <Message /> elements is updated back into MessageBox's state. The reason for this is to keep all the recent updated data in one place only. The above approach creates havoc if shouldComponentUpdate() method in <Message /> is not overloaded (infinite recursion).
Is there a better approach for this? I've to write a lot of code just to override the builtin methods to keep the entire thing stable. As I'm not planning to go for Flux/Redux, is there a React-only approach for this?
EDIT: Since there's a lot of confusion, I'm adding minimal code.
class Message extends React.Component {
constructor(props) {
this.state = {
subject: this.props.subject,
body: this.props.body,
type: this.props.type,
messageIndex: this.props.messageIndex
};
}
componentDidUpdate() {
this.props.updateHandler(messageIndex, {
subject: this.state.subject,
body: this.state.body,
type: this.state.type
});
}
render() {
return (
<div>
<input
type="text"
defaultValue={this.state.subject}
onBlur={e => this.setState({subject: e.target.value})} />
<input
type="text"
defaultValue={this.state.subject}
onBlur={e => this.setState({body: e.target.value})} />
<select
type="text"
value={this.state.subject}
onChange={e => this.setState({type: e.target.value})}>
<option>Type 1</option>
<option>Type 2</option>
</select>
</div>
)
}
}
class MessageBox extends React.Component {
constructor(props) {
this.state = {
messages: aListOfMessageObjects
}
}
updateHandler(message) {
// Message update happens here and returns a list updatedMessages
this.setState({
messages: updatedMessages
});
}
render() {
let _this = this;
var messagesDOM = this.state.messages.map((m) => {
return (
<Message
message={m}
updateHandler={_this.updateHandler.bind(_this)} />
);
})
return (
<div>
{messagesDOM}
</div>
);
}
}
If that can help, read thinking-in-react. It explains how data should go only one way to avoid be lost in UI updates.
React ToDo MVC will provide you an example of React good practice on a real case
To know how to pass props from your parent to children read controlled-components. you'll have to use value and onBlur on each input. Any onBlur event will call this.props.updateHandler with e as parameter instead of e => this.setState({type: e.target.value}).
Don't do a callback to MessageBox from componentDidUpdate() of Message. Do a callback directly from an action in Message.
You don't need state in Message component at all. Props will keep the values you are interested if you update parent's state properly.
What you need is something like:
<input type="text"
defaultValue={this.props.subject}
onBlur={e => this.updateSubject(e.target.value)} />
updateSubject: function(newSubjectValue) {
this.props.updateHandler(messageIndex, {
subject: newSubjectValue,
body: this.props.body,
type: this.props.type
});
}
That way the component will get re-rendered, but won't do another call to the parent's setState.

How to access a child's state in React

I have the following structure:
FormEditor - holds multiple instances of FieldEditor
FieldEditor - edits a field of the form and saving various values about it in its state
When a button is clicked within FormEditor, I want to be able to collect information about the fields from all FieldEditor components, information that's in their state, and have it all within FormEditor.
I considered storing the information about the fields outside of FieldEditor's state and put it in FormEditor's state instead. However, that would require FormEditor to listen to each of its FieldEditor components as they change and store their information in its state.
Can't I just access the children's state instead? Is it ideal?
Just before I go into detail about how you can access the state of a child component, please make sure to read Markus-ipse's answer regarding a better solution to handle this particular scenario.
If you do indeed wish to access the state of a component's children, you can assign a property called ref to each child. There are now two ways to implement references: Using React.createRef() and callback refs.
Using React.createRef()
This is currently the recommended way to use references as of React 16.3 (See the documentation for more information). If you're using an earlier version then see below regarding callback references.
You'll need to create a new reference in the constructor of your parent component and then assign it to a child via the ref attribute.
class FormEditor extends React.Component {
constructor(props) {
super(props);
this.FieldEditor1 = React.createRef();
}
render() {
return <FieldEditor ref={this.FieldEditor1} />;
}
}
In order to access this kind of ref, you'll need to use:
const currentFieldEditor1 = this.FieldEditor1.current;
This will return an instance of the mounted component so you can then use currentFieldEditor1.state to access the state.
Just a quick note to say that if you use these references on a DOM node instead of a component (e.g. <div ref={this.divRef} />) then this.divRef.current will return the underlying DOM element instead of a component instance.
Callback Refs
This property takes a callback function that is passed a reference to the attached component. This callback is executed immediately after the component is mounted or unmounted.
For example:
<FieldEditor
ref={(fieldEditor1) => {this.fieldEditor1 = fieldEditor1;}
{...props}
/>
In these examples the reference is stored on the parent component. To call this component in your code, you can use:
this.fieldEditor1
and then use this.fieldEditor1.state to get the state.
One thing to note, make sure your child component has rendered before you try to access it ^_^
As above, if you use these references on a DOM node instead of a component (e.g. <div ref={(divRef) => {this.myDiv = divRef;}} />) then this.divRef will return the underlying DOM element instead of a component instance.
Further Information
If you want to read more about React's ref property, check out this page from Facebook.
Make sure you read the "Don't Overuse Refs" section that says that you shouldn't use the child's state to "make things happen".
If you already have an onChange handler for the individual FieldEditors I don't see why you couldn't just move the state up to the FormEditor component and just pass down a callback from there to the FieldEditors that will update the parent state. That seems like a more React-y way to do it, to me.
Something along the line of this perhaps:
const FieldEditor = ({ value, onChange, id }) => {
const handleChange = event => {
const text = event.target.value;
onChange(id, text);
};
return (
<div className="field-editor">
<input onChange={handleChange} value={value} />
</div>
);
};
const FormEditor = props => {
const [values, setValues] = useState({});
const handleFieldChange = (fieldId, value) => {
setValues({ ...values, [fieldId]: value });
};
const fields = props.fields.map(field => (
<FieldEditor
key={field}
id={field}
onChange={handleFieldChange}
value={values[field]}
/>
));
return (
<div>
{fields}
<pre>{JSON.stringify(values, null, 2)}</pre>
</div>
);
};
// To add the ability to dynamically add/remove fields, keep the list in state
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditor fields={fields} />;
};
Original - pre-hooks version:
class FieldEditor extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const text = event.target.value;
this.props.onChange(this.props.id, text);
}
render() {
return (
<div className="field-editor">
<input onChange={this.handleChange} value={this.props.value} />
</div>
);
}
}
class FormEditor extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleFieldChange = this.handleFieldChange.bind(this);
}
handleFieldChange(fieldId, value) {
this.setState({ [fieldId]: value });
}
render() {
const fields = this.props.fields.map(field => (
<FieldEditor
key={field}
id={field}
onChange={this.handleFieldChange}
value={this.state[field]}
/>
));
return (
<div>
{fields}
<div>{JSON.stringify(this.state)}</div>
</div>
);
}
}
// Convert to a class component and add the ability to dynamically add/remove fields by having it in state
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditor fields={fields} />;
};
ReactDOM.render(<App />, document.body);
As the previous answers said, try to move the state to a top component and modify the state through callbacks passed to its children.
In case that you really need to access to a child state that is declared as a functional component (hooks) you can declare a ref in the parent component, and then pass it as a ref attribute to the child, but you need to use React.forwardRef and then the hook useImperativeHandle to declare a function you can call in the parent component.
Take a look at the following example:
const Parent = () => {
const myRef = useRef();
return <Child ref={myRef} />;
}
const Child = React.forwardRef((props, ref) => {
const [myState, setMyState] = useState('This is my state!');
useImperativeHandle(ref, () => ({getMyState: () => {return myState}}), [myState]);
})
Then you should be able to get myState in the Parent component by calling:
myRef.current.getMyState();
It's 2020 and lots of you will come here looking for a similar solution but with Hooks (they are great!) and with the latest approaches in terms of code cleanliness and syntax.
So as previous answers had stated, the best approach to this kind of problem is to hold the state outside of child component fieldEditor. You could do that in multiple ways.
The most "complex" is with a global context (state) that both parent and children could access and modify. It's a great solution when components are very deep in the tree hierarchy and so it's costly to send props in each level.
In this case I think it's not worth it, and a more simple approach will bring us the results we want, just using the powerful React.useState().
An approach with a React.useState() hook - way simpler than with Class components
As said, we will deal with changes and store the data of our child component fieldEditor in our parent fieldForm. To do that we will send a reference to the function that will deal and apply the changes to the fieldForm state, you could do that with:
function FieldForm({ fields }) {
const [fieldsValues, setFieldsValues] = React.useState({});
const handleChange = (event, fieldId) => {
let newFields = { ...fieldsValues };
newFields[fieldId] = event.target.value;
setFieldsValues(newFields);
};
return (
<div>
{fields.map(field => (
<FieldEditor
key={field}
id={field}
handleChange={handleChange}
value={fieldsValues[field]}
/>
))}
<div>{JSON.stringify(fieldsValues)}</div>
</div>
);
}
Note that React.useState({}) will return an array with position 0 being the value specified on call (Empty object in this case), and position 1 being the reference to the function
that modifies the value.
Now with the child component, FieldEditor, you don't even need to create a function with a return statement. A lean constant with an arrow function will do!
const FieldEditor = ({ id, value, handleChange }) => (
<div className="field-editor">
<input onChange={event => handleChange(event, id)} value={value} />
</div>
);
Aaaaand we are done, nothing more. With just these two slim functional components we have our end goal "access" our child FieldEditor value and show it off in our parent.
You could check the accepted answer from 5 years ago and see how Hooks made React code leaner (by a lot!).
Hope my answer helps you learn and understand more about Hooks, and if you want to check a working example here it is.
Now you can access the InputField's state which is the child of FormEditor.
Basically, whenever there is a change in the state of the input field (child), we are getting the value from the event object and then passing this value to the Parent where in the state in the Parent is set.
On a button click, we are just printing the state of the input fields.
The key point here is that we are using the props to get the input field's id/value and also to call the functions which are set as attributes on the input field while we generate the reusable child input fields.
class InputField extends React.Component{
handleChange = (event)=> {
const val = event.target.value;
this.props.onChange(this.props.id , val);
}
render() {
return(
<div>
<input type="text" onChange={this.handleChange} value={this.props.value}/>
<br/><br/>
</div>
);
}
}
class FormEditorParent extends React.Component {
state = {};
handleFieldChange = (inputFieldId , inputFieldValue) => {
this.setState({[inputFieldId]:inputFieldValue});
}
// On a button click, simply get the state of the input field
handleClick = ()=>{
console.log(JSON.stringify(this.state));
}
render() {
const fields = this.props.fields.map(field => (
<InputField
key={field}
id={field}
onChange={this.handleFieldChange}
value={this.state[field]}
/>
));
return (
<div>
<div>
<button onClick={this.handleClick}>Click Me</button>
</div>
<div>
{fields}
</div>
</div>
);
}
}
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditorParent fields={fields} />;
};
ReactDOM.render(<App/>, mountNode);
You may access the child state by passing a callback to the child component.
const Parent = () => {
return (
<Child onSubmit={(arg) => {
console.log('accessing child state from parent callback: ', arg)
}}
/>
)
}
const Child = ({onSubmit}) => {
const [text, setText] = useState('');
return (
<>
<input value={text} onChange={setText}>
<button onClick={() => onSubmit(text)} />
</>
)
}
Now if you click the button in the child component, you will execute the function passed from the parent and have access to the child component's state variables.

Categories

Resources