Having trouble with setting state to value of "Enter name" field - javascript

I have a simple TextInput for entering your name. I want to pass this to another scene. First, though, I want to save the value to the state of the current component. When I open up the debugger, I get undefined for every console.log(this.name) I have.
Here is my code:
constructor(props) {
super(props);
this.state = {
name: ''
};
}
<TextInput style={styles.inputName}
placeholder="Enter your name"
textAlign="center"
onChange={this.handleChange.bind(this)} />
handleChange(event) {
this.setState({
name: event.nativeEvent.text
});
console.log(this.name)
}
Do you know why I am always getting "undefined" as the value of name? What is the proper way to set the state equivalent to what is being typed?
Is a better approach to set state only after the submit button is pressed? If so how is that done?

You're attempting to access this.name, which is checking for a (likely nonexistent) name property on the component object in your case. You should be looking at this.state.name. However, even if you did attempt to log out the updated state in your event handler via this.state.name, you would probably still run into problems. From the React docs:
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value.
Finally, I would suggest extracting the name value from your event through event.target.value rather than going through the nativeEvent property.

Related

Referencing state from within state - React js

Is it possible to reference state from within state to create an id. e.g id = this.state.name + this.state.desc
Something like this?
this.state = {name:'', desc:'', id:`${this.state.name}-${this.state.desc}`}
Nope, that won't work. this.state is initialized before the constructor, but it won't like that you are referencing an undefined portion of the object. You would need to set it after initialization. I'm assuming you would be using this.state = {blah} from your constructor. Otherwise, you should use this.setState().
However this is also bad practice because anytime you update your name or desc, it will update the state again with your new id value. I don't know the full scope of your code, but you can probably just save the id to a function that gives you the string.
Remember you need to set the state using setState
So
this.setState({ id:`${this.state.name}-${this.state.desc}` })
This is also assuming that you have already set the state in the constructor (or somewhere else int he app).
If you are in the constructor, then this should work just fine...
this.state = { name: 'Toli', desc: 'A programmer' };
this.state = {...this.state, id:`${this.state.name}-${this.state.desc}`}
I'm assuming you want to keep name and desc in the state so I'm copying them using the spread op (...this.state)

How does `onChangeText={(text) => this.setState({text})` work?

On https://facebook.github.io/react-native/docs/handling-text-input they give an example of using onChangeText with:
<View style={{padding: 10}}>
<TextInput
style={{height: 40}}
placeholder="Type here to translate!"
onChangeText={(text) => this.setState({text})}
/>
I don't exactly understand why this.state.text is set to the value of TextInput. How exactly does it work? I understand anonymous function is used here, but still can't see where this.state.text is initiated. Can you show an example of a similar thing with plain javascript?
Thanks a lot!
but still can't see where this.state.text is initiated
it's actually initialized in the constructor
constructor(props) {
super(props);
this.state = {text: ''}; // initialized here
}
I don't exactly understand why this.state.text is set to the value of TextInput
whenever the user type text in the input, the onChangeText function is called which actually update the state => the text variable will take the value of the text input
Moreover, when you call setState it will re-render the component, so
<Text style={{padding: 10, fontSize: 42}}>
{this.state.text.split(' ').map((word) => word && 'πŸ•').join(' ')}
</Text>
will be re-executed and you will see a πŸ• symbol for each word typed by the user
The onChangeText is listening for changes to the input. That is your text in regards to (text). We then use es6 arrow notation to call an anonymous function => . From here we call this.setState({}) , which is a built in state management function with react. It sets the variable, (which may or may not be declared) in state to the changed value of (text) to be used later or elsewhere.
The confusion might be that if you didn't declare text yourself, as such,
this.state = { text: ''}
It would be confusing how its actually storing if you didn't have text already in your state, but saw it updating. But in react, it will create a new state variable even if you didn't declare one automatically.
Only place you directly write to this.state should be the Components constructor. In other places you should be using this.setState function, which accepts an Object that is merged into Components current state.
It's possible to alter state by writing to this.state directly, However it will not lead to Component re-rendering with new data, and generally lead to state inconsistency.
this.state.text is part of the state of the component.
I don't exactly understand why this.state.text is set to the value of TextInput
It is stored in the state so that other components can access the value and use it. (For example: Form submission needs all values.)
For a TextInput form submission example :
handleSubmit = () => {
mySubmitService(this.state.textInput)
}
Another example from the react docs :
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()}; //This is the default state initialization
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2> //This is accessing the value
</div>
);
}
}
Plain JS :
var data = +(document.getElementById("TextInputId").value;);
I understand anonymous function is used here, but still can't see where this.state.text is initiated.
That's just example of code. You must specify initial state to make onChangeText work inside the constructor. FYI, that's not just an anonymous function, but an arrow function.
this.state = {
text: ''
}
Can you show an example of a similar thing with plain javascript?
I think you meant to use in other way? If so, you can use like this:
Define a method:
onChangeText(text) {
this.setState({text})
// ^^ alias of -> this.setState({text:text})
}
Use onChangeText method:
onChangeText={this.onChangeText(text)}
Bind this inside the constructor:
this.onChangeText = this.onChangeText.bind(this)

React.js: Should parent or component manage state?

I have a "Date input" component that contains a <input type="text">.
It's purpose is to allow users to type in a valid date (e.g., mm/dd/yyyy).
Once a user enters a valid date, the parent of the component should receive that date.
Based on this - I'm trying to figure out whether the component or the parent should manage state.
If I set it up so the parent manages state:
render() {
return (
<div>
<input type="text" value={this.props.value} onChange={this.props.handleChange} />
</div>
);
}
... this is no good, because the parent will be notified with every single change, and the prop will be set to all the "draft" values (e.g., "07/2") while the user is typing.
That suggests that I should set this up so that the component manages it's own state:
class InputDateWithLabel extends Component {
constructor(props) {
super(props);
this.state = {
value: formatDate(this.props.value) // formatDate() formats a date object into an 'mm/dd/yyyy' string
};
this.handleChange = this.handleChange.bind(this);
this.handleBlur = this.handleBlur.bind(this);
}
handleChange(event) {
// use setState() to update this.state.value so the <input> shows what the user types
}
handleBlur(event) {
// if the user entered a valid date,
// convert the user's input to a date object,
// and communicate that to the parent. E.g.,:
this.props.onChange([new date]);
// Otherwise, handle the "error"
}
render() {
return (
<input type="text" value={this.state.value} onChange={this.handleChange} onBlur={this.handleBlur} />
);
}
}
This version works exactly the way I want it to, except for one more requirement...
Based on things that might happen elsewhere in my application, the parent may need to set the date in this component. However - now that the component is managing it's own state - when the parent changes props.value, my component will ignore it.
The React documents address this scenario here: You Probably Don't Need Derived State
But their solutions don't seem to apply:
One way to avoid the problems mentioned above is to remove state from our component entirely.
This is no good, because I don't want to make the parent responsible for validating the user's date input. My component should be self-contained, including the date validation, which means it needs to manage the "draft states" of the user's input.
Another alternative would be for our component to fully own the β€œdraft” state. In that case, our component could still accept a prop for the initial value, but it would ignore subsequent changes to that prop
This is no good, because I need to retain the ability for the parent to change the value when appropriate.
The rest of the React documentation mentions a few other possibilities (getDerivedStateFromProps), but it goes to great lengths to stress that they're probably not correct. (Note the title of the article!)
This does not seem like an uncommon situation, so there must be a clear, simple, well-documented way to handle it, that's done the right "React-way". What is that?
Having a component manage it's own state doesn't seem that bad in your case, but you will need to add componentWillReceiveProps which adds another piece of code to manage.
componentWillReceiveProps(nextProps) {
this.setState({
value: formatDate(nextProps.value)
});
}

defaultValue of react not working in meteor

I am trying to figure this out, I don't know why this isn't working
<input type="text" id="first_name" name="first_name" className="form-control" defaultValue={this.props.user.first_name} required/>
but this works
<input type="text" id="first_name" name="first_name" className="form-control" value={this.props.user.first_name} required/>
the difference is value and defaultValue, if I use value the field becomes readonly and using defaultValue doesn't print any thing.
I am using react with meteor. I have tried logging this.props.user in render method before the return statement and it prints the object.
When you assign this.props.user.first_name to the value attribute it's not that the input field is becoming read-only, it's that you are never handling what happens when that value changes. React is simply re-rendering it with the value you directly assigned to it each time.
If you are looking to make the field editable + have the default user name value you should probably maintain and be aware of the state of the input.
So for example:
// Initialize some component state in either your constructor or getInitialState function
constructor(props){
super(props);
this.state = {userInput: this.props.user.first_name};
}
// Have a function that updates state when your input changes
onInputChange(event) {
this.setState({ userInput: event.target.value });
}
// Then set the value equal to your userInput state and add an onChange
// prop to the input tag in your render method.
render() {
return (
...
<input
type="text"
id="first_name"
name="first_name"
className="form-control"
value={this.state.userInput}
onChange={this.onInputChange.bind(this)} />
)
}
Then the value for the field initializes to the value it's being provided through this.props.user.first_name, while also remaining editable.
Edit:
As pointed out in the comments, while valid, this is actually an anti-pattern in React. Because the initial state of the child component is only called once, a change from a parent to the prop value of this.props.user.first_name will not cause any change in the state of the child. This is fine if the use case is to expressly set an initial value that you do not want or expect to change during the component life-cycle (though even then it's not a great pattern), but if you do expect the initial value to be mutable you have two options.
Option One: Bring the state up into the parent component, where it likely belongs. The child component should then receive and render any props that are sent it's way. Changes to the initial value are handled in the parent component state, props are treated as immutable, and everything stays in sync.
Option Two: If for whatever reason you both need to determine state from props and you also expect those props to change, you can make use of the componentWillReceiveProps(nextProps) life-cycle method to keep everything in sync. This will allow you to check this.props against nextProps and make any state changes if they are necessary:
componentWillReceiveProps(nextProps) {
if(nextProps.user.first_name !== this.props.user.first_name){
this.setState({
userInput: nextProps.user.first_name
});
}
}
Here's a link to the DOCS for further reference.

Have React component trigger a change event the way DOM components would

I have a React Component that wraps an <input type='text' /> but only calls onChange when the value has changed to something valid. (Its own state allows it to change the text value of the input, but when it calls onChange, it actually returns an object of the parsed value of the input.)
export default class MyInputClass extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = { textValue: '' };
// ...
}
// ...
handleChange(e) {
// set the state regardless of value so the input's text changes
this.setState({ textValue: e.target.value});
// determine whether the value is valid, and if so, call onChange()
let myObj = MyParsedObject.parse(e.target.value);
if (myObj !== null && this.props.onChange) {
this.props.onChange(myObj);
}
}
render() {
return <input onChange={this.handleChange} value={this.state.textValue} />;
}
}
// ...
MyInputClass.propTypes = {
onChange: React.PropTypes.func
};
Right now I have a property onChange, which is a React.PropTypes.func, and when the input's change is triggered, I attempt to parse the input's value. If it's successful, I check to see if this.props.onChange is not null, and then call onChange(myParsedObject).
This works, but it doesn't feel right. Handlers of onChange should expect an Event parameter (or SyntheticEvent in React), not an object. Further, this pattern only allows one handler for onChange, whereas real Events can have multiple handlers.
How are you supposed to design React Components to emit real events?
If MyInputClass is designed to be a generic wrapper around inputs, it might make sense to call this.props.onChange with the event, rather than the parsed input, and let the parent component decide how to parse it. If, however, MyInputClass is a wrapper for a specific type of input, it might make sense to also pass on the parsed value. You could always do both, and make it an explicit part of the API:
this.props.onChange(e, myObj);
Or use onChange as the generic handler and onWhateverChange as the parsed version; for example, for a JsonInput component, you might do
this.props.onChange(e);
this.props.onJsonChange(parsedJson);

Categories