Input field needs to both controlled and uncontrolled - javascript

I have an input field which is rendered like this:
class InputSection extends Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
}
handleChange(event) {
this.props.onChange(event.target.name, event.target.value);
}
render() {
return (
<input type="text"
name="topicInputText"
value={this.props.formValues['topicInputText']}
onChange={this.props.onChange}
/>
);
}
}
There are two (perceived) reasons why I have value bound to an entry of the this.props.formValues object:
I need to pass in a default value
I need the ability to reset the input field when the users clicks on a "reset" button. This reset button triggers a redux action which leads to a change in the formValues object passed-in as a prop
The onChange event handler, which is handled up the component chain, triggers a redux action which updates the store state that drives the formValues object passed-in as a prop.
But when I type into the input field using the keyboard I get this console warning:
Warning: InputSection is changing an uncontrolled input of type text
to be controlled. Input elements should not switch from uncontrolled
to controlled (or vice versa). Decide between using a controlled or
uncontrolled input element for the lifetime of the component.
I suppose this is because my input component is being "managed" (since I'm binding it's value). But, if I can't bind it's value how do I set it's initial value or reset the value at a later time?

That generally means you're switching between giving it undefined and defined values. I'm guessing that formValues["topicInputText"] does not exist when this is first rendered.
You'll need to make sure that the initial value you pass in is not undefined.

Related

How to override a components props where the component itself is a prop

I have the following component which receives a prop called button.
This button prop is actually a Component itself and within it there are some props.
I wish to override the prop called type within there. Is this possible?
// I only have access to this class and looking to work within here.
// button is the component below.
// I want to override the prop type that is being passed in there.
const MyClass = ({
button
}) => {
// tried following but throws synax errors
// return props => <button type={'overridenValue'} {...props} />;
return button;
};
export default MyClass;
// I do not have access to this class
// nor the point at which this is created.
const Button = ({type}) => (
<div></div>
);
export default Button;
Edit:
Elaborating on what I am trying to achieve.
As mentioned above I am receiving a component which has a set of props already in it. I wish to override / replace / or make a copy of this component so that I can change one of the props in this component.
That prop is called type. Ultimately the prop is a Type of color. Like Type.White or Type.Blue and so on. When I receive the component, it comes with the value Type.White. I want to change it to Type.Blue. As the name suggests it changes the color of the button.
The button is a lot more complex with more props in it with routing details and others. Thus I need those details to be unchanged. I only want to change the Type value.
You can use cloneElement to achieve this. The second parameter for cloneElement is a props object that will let you do exactly what you're asking:
The resulting element will have the original element’s props with the new props merged in shallowly.
Here's a simple demo in StackBlitz. The caller is passing an input of type text to the wrapper -
<Wrapper component={<input id="component" type="text" data-name="some-name" />} />
The wrapper then creates a clone of this element and overrides the type to be a checkbox:
const { component } = props;
const nextProps = { type: 'checkbox' };
const Component = React.cloneElement(component, nextProps);
Then in the rendered output, you can see that the properties from the original input have been passed, but the type has been replaced:
<input id="component" type="checkbox" data-name="some-name">

How to assign value to a prop NOT inside onChange in ReacJS?

I have a multi-step Form components and I need to update a state that been sent from a different component.
in the first component I have states:
export class BookForm extends Component {
constructor(){
super()
this.state = {
service: '',
price: ''
Now I have this method:
handleChange = input => event => {
this.setState({
[input]: event.target.value
});
}
I pass this method with the other props to a diffrent component and whenever "onChange" happends I call it with the name of the prop inside like this:
onChange={this.props.handleChange("service")}
That way the onChange event is assiging to the prop.
I have a problem because I have a value that is coming back from an API and I need to assign it to a prop.
I'm having trouble to assign the value inside the prop from the second component without 'onChange'.
Is there a method I can pass to the component that I can use? The function I showed here works with onChange.
Thanks a lot!
Edit:
This is the part from the child component:
<select
class="form-drp-btn"
onChange={this.props.handleChange("service")}
onInput={(e) => {this.setPrice(e)}}
>
the steps are like this:
with "onChange" I get the service that been chosen. with "onInput" I get the price that coming back from the API and belong to the certain service.
Now, I want to assign this value to the "price" state that came from the father component.

Creating controlled input in react

I'm a newbie and learning React with FreeCodeCamp. On this challenge it says:
When you type in the input box, that text is processed by the
handleChange() method, set as the input property in the local state,
and rendered as the value in the input box on the page. The component
state is the single source of truth regarding the input data.
I wrote this solution:
class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
// Change code below this line
this.handleChange = this.handleChange.bind(this);
// Change code above this line
}
// Change code below this line
handleChange(event) {
this.setState({
input: event.target.value
})
}
// Change code above this line
render() {
return (
<div>
{ /* Change code below this line */}
<input value={this.state.input} onChange={this.handleChange()} />
{ /* Change code above this line */}
<h4>Controlled Input:</h4>
<p>{this.state.input}</p>
</div>
);
}
};
the console says:
“Build error, open your browser console to learn more.”
Where am I doing wrong? I cannot see my mistake..
The issue is related to how you assign the event handler to onChange.
onChange expects a callback function which will be fired when value of the input is changed. With onChange={this.handleChange()} in your post, you actually assigns undefined to onChange since handleChange function update the component state but it doesn't return anything.
Change it to onChange={this.handleChange} does what you expect it to work. When input is changed, this.handleChange will be called and event object will be passed in as parameter.
Hopefully that helps.
You're calling handleChange instead of passing its reference as the onChange prop.
You likely do not need to run your handleChange method in the onChange prop. So you would have this instead:
onChange={this.handleChange}

React - setting input value with JavaScript does not trigger 'onChange'

In my React app (version 15.4.2), I am updating the value of a text input field with JavaScript - however, I have an onChange event listener associated with the input field, and changing the value of the input field does not trigger the handler (good old fashioned typing in the input field does, of course), though the content of the field is correctly updated.
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
}
onChange(event){
let attribute = event.target.name;
let updatedGroup = this.state.group;
updatedGroup[attribute] = event.target.value;
this.setState({group: updatedGroup});
}
addMember(memberId) {
let inputField = document.getElementById("members");
let inputValues = inputField.value.split(",");
inputField.value = [...inputValues, memberId];
}
render(){
<input type="text" id="members" name="members" value={this.state.group.members} onChange={this.onChange} />
}
So when addMember() is called (via button click in a child component), then content of the input field itself is correctly updated, but onChange is not called and thus the state is not updated, etc...
Is there a way that I can programatically set the value of the input field and trigger onChange?
What I always do in this situation is have your onChange event handler be a function that passes the event data (the character that was entered or the aggregate string) into another function. I put all of the business logic in that function. That way, if I want to invoke the business logic, I just call that method.
Since you are asking "Is there a way that I can programatically set the value of the input field and trigger onChange?" Why not skip onChange and call the business logic function from your function that is programmatically setting the value?
Onchange method will get triggered only when you type something, if you use document.getElementById and replace its values, it will directly replace the value in DOM, onChange will not get triggered in that case. Since you are using the react, I think you should avoid the direct DOM manipulation.
You are using controlled input, so in addMember method instead of updating the value in DOM, update the state value.
Try this addmember method:
addMember(memberId) {
//let inputField = document.getElementById("members");
//let inputValues = inputField.value.split(",");
//inputField.value = [...inputValues, memberId];
let group = this.state.group.slice();
group[members] = group[members] + ',' + memberId;
this.setState({
group
});
}

React - Best practice with state in form components?

I'm trying to get my head around best practice regarding state in react components. I started creating a form by writing a TextField component as follows
var TextField = React.createClass({
render: function() {
const {value, title, placeholder} = this.props;
return (<div>
{title}
<input type="text"
value={value}
placeholder={placeholder}
onChange={this.handleChange} />
</div>);
},
handleChange (evt){
this.props.onChange(evt.target.value);
}
});
This is a controlled component. So the parent container has to pass a value in for the input in via props and change that value when there is a change. It seems like this is the usual approach.
My problem comes when I want to create a numeric field. For this example assume that my numeric field will allow non numeric characters to be input (the field just won't validate). I don't like the idea of having the validation of that field within the parent so this is what I wrote
var NumericField = React.createClass({
getInitialState: function(){
return{
value : ""
}
},
componentWillReceiveProps: function(nextProps) {
if(this.validate(nextProps.value)){
this.setState({value:nextProps.value});
}
},
validate : function(input) {
return !isNaN(input);
},
render: function() {
const {value} = this.state;
const {title} = this.props;
return (<div>
{title}
<input type="text"
value={value}
onChange={this.handleChange} />
</div>);
},
handleChange (evt){
this.setState({value:evt.target.value});
if(this.validate(evt.target.value)){
this.props.onChange(evt.target.value);
}
}
});
This allows the parent to set the value and update it via the "value" prop, but the "onChange" prop will only trigger when the content is valid. It feels to me like I've used a different pattern for each, and that's not good. Not sure if that's a valid feeling?
I suppose I just wanted to ask if my approach to the numeric field seems reasonable or if there is a better pattern to follow?
Just in case someone wants to know why I want a numeric field to work this way, I don't, it's just a simplified example. A more valid example would be a text area for json, that only called onChange when the content was valid json.
Appreciate any feedback
Setting state by passing in props is generally frowned upon.
Props should be immutable (like your NumericField component's title)
If you want to set an initial value it should come from the controller or store the parent component is getting it from, eg.
getInitialState() {
return({
value: FormDataStore.getInitialNumericFieldValue()
});
}
After that, any changes to the value should be handled by the NumericField component. If you need to validate do so before setting the new state, eg.
handleChange(evt) {
if (this.validate(evt.target.value)){
this.setState({
value: evt.target.value
});
/* You can also pass the new validated value
up to the parent component to hold on to
till you're ready to process the form*/
this.props.onChange(evt.target.value);
}
}
Your state will now only ever hold (and subsequently the parent component will only ever receive) the last validated value so you could even display valid/invalid message if this.state.value === input, but that's extra
Incidentally, your TextField component should also follow this pattern. Passing a changed value up to the parent just to have it passed down again as a prop defeats the purpose of having a child component. In that case I would have the JSX (and any validation process) all in the parent component instead of abstracting a child. Unless the child component could be re-used. But then I'd still let the child handle its own state.

Categories

Resources