Obtain value from InputText using React.createRef() - javascript

Instead of re-rendering the entire component-tree whenever "<InputText style{...}>" is changed, I am trying to use refs in my Class Component. (I am using React Native with Expo managed workflow.)
Using refs, the typed text appears as it should in the InputText field.
But, when a button is pressed, the value of the typed text (value of the InputText) should be console logged, however it is not.
export class Feed extends Component {
constructor(props){
super(props);
this.state = {
//some state variables
}
this.myTextFromInput = React.createRef()
}
I started by creating the myTextFromInput ref (above).
<TextInput
style={{height:100}}
ref={this.alias}
placeholder="Input Text Here"
/>
I then used the myTextFromInput ref in the InputText component. And lastly, the button!
<Button onPress={()=>console.log(this.myTextFromInput.current.value)}>Press me!</Button>
This gives me undefined. I have also tried this.myTextFromInput.value and a .getText() method which is outdated.
How can I obtain the inputed text?
UPDATE:
Terminal log undefined. But snack works fine!?

You aren't passing the correct reference to TextInput, it should be this.myTextFromInput not this.alias, take a look:
export class Feed extends Component {
constructor(props){
super(props);
this.state = {
//some state variables
}
this.myTextFromInput = React.createRef()
// bind the method to the component instance
this.logInputText = this.logInputText.bind(this)
}
logInputText() {
console.log(this.myTextFromInput.current.value)
}
render() {
return (
<View>
<TextInput
style={{height:100}}
// pass the correct reference here
ref={this.myTextFromInput}
placeholder="Input Text Here"
/>
<Button
onPress={this.logInputText}>
Press me!
</Button>
</View>
)
}
}
Also don't forget that whether you use a method instead of arrow function you've to bind it to the class instance, like I did. See this question and this example from react docs.
Update: React.ref on Android and iOS doesn't seems to work as the same way as it works on web, you can't get the value from input because the component doesn't provide this property, doing a console.log(this.myTextFromInput.current) you can see all the available properties. One solution from this question is to use TextInput from the package react-native-paper, as it provides the input value from the ref, or you could use the common state approach to store the input value, like so:
export class Feed extends Component {
constructor(props){
super(props);
this.state = {
myTextFromInput: ""
}
// bind the method to the component instance
this.logInputText = this.logInputText.bind(this)
}
logInputText() {
console.log(this.state.myTextFromInput)
}
render() {
return (
<View>
<TextInput
style={{height:100}}
// you don't need to use ref
placeholder="Input Text Here"
onChangeText={(text) => this.setState({myTextFromInput: text})}
/>
<Button
onPress={this.logInputText}>
Press me!
</Button>
</View>
)
}
}

Related

Cannot read property 'someProperty' of undefined in react

I am working on an application where I pass variable values in a Navlink using state from one component to the other and then load those received values in input fields and click on submit button in that other component to do something with values. My values are received correctly and show up correctly when I alert them. But when I click submit button, it gives error,pointing at the constructor
TypeError: Cannot read property 'id' of undefined
Here is my code
class Parent extends React.Component{
constructor(props) {
super(props);
this.state={id:2}
}
render(){
return(
<NavLink
to={{
pathname: '/Child',
state: {
id: this.state.id
}
}}
>
Edit
</NavLink>
)
)
}
Where I receive the values
class Child extends React.Component{
constructor(props) {
super(props);
this.state = {id:this.props.location.state.id}
alert(this.props.location.state.id)//works fine
}
setId(e){
this.setState({id:e.target.value})
}
addOrEdit(){ //gives error
alert(this.state.id)
//do something
}
render(){
return(
<div>
<form>
<label>Id</label>
<input value={this.state.id} onChange={this.setId.bind(this)} type="text"/><br/>
<input type="submit" onClick={this.addOrEdit.bind(this)} ></input>
</form>
</div>
)
}
}
this.state = {id: this.props.location && this.props.location.state && this.props.location.state.id}
Should fix your issue that caused by times that this component called without this context or this line got excuted before location set.
(assuming you using withRouter for making location props be exist...)
Anyhow, and not related directly to your issue, it is bad practice to set initial value for state from props at constructor, consider manipulate state through life cycle either don't use state here and refer to props directly
I would suggest to just use arrow functions for setId and addOrEdit.
addOrEdit = (e) => {
// ...
}
And just call them:
onChange={this.setId}
onClick={this.addOrEdit}
https://medium.com/#machnicki/handle-events-in-react-with-arrow-functions-ede88184bbb
Also you are deriving state from prop.
It is better to just use the prop directly.
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

Input text inside a react component

I'm trying to create an input text inside a react component and then I realised that it's a bad praxis. So I investigated a little bit so I found Controlled-Components, so I think this is what I need, but looking at my Component I do not know how to create it.
I do not have an extends Redux.Component so a friend suggested me to create a Component but couldn't get succeed.
What I was trying is this :
Inside my component
<input
...
/>
{" "}
<input
...
/>
<span>
<myButton
...
arguments={[document.getElementById("id1").value, document.getElementById("id2").value]}
>
[ send ]
</myButton>{" "}
</span>
But I'm getting this error :
The given id must not be null!; nested exception is java.lang.IllegalArgumentException: The given id must not be null!
EDIT
On my component where I have all of those code I have this :
<myButton
id={id}
arguments={[intputStuff]}
>
So my problem is if I do what Tom's says I do not have the id in the other component.
So the thing should be create this component inside the other component and then get the values of the inputtexts and put them as an arguments
It's not clear from your post what exactly you're trying to accomplish.
It appears that you're trying to build a component with 2 text inputs and a button.
If you want the button to "submit" the values of the two inputs, you should do something like this:
class SomeComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
value1: props.initialValue1,
value2: props.initialValue2
}
}
onChangeText = (event) => this.setState({ [event.target.name]: event.target.value })
onClickSubmit = (event) => {
let { onSubmit } = this.props
if(typeof onSubmit !== 'function') return
let { value1, value2 } = this.state
return onSubmit([ value1, value2 ])
}
render() {
let {
initialValue1,
initialValue2,
onSubmit,
className,
...props
} = this.props
let {
value1,
value2
} = this.state
return (
<div className={`SomeComponent ${className}`} {...props}>
<input value={value1} name="value1" onChange={this.onChangeText} />
<input value={value2} name="value2" onChange={this.onChangeText} />
<button onClick={this.onClickSubmit}>
Submit
</button>
</div>
)
}
}
A few notes:
This example uses a bunch of futuristic JS: destructuring, rest/spread, class properties, computed property names, and arrow functions. Each feature is being leveraged for a specific purpose, not just because they're cool. If your environment doesn't support some of these features, you'll need to find a workaround that makes good on some additional constraints.
This is not a controlled component, but it does contain 2 controlled inputs. It uses the "initialValue" pattern: the owning component provides starting values, but is unaware of the blow-by-blow as the user types each character. The owning component is only notified of the new values when the button is clicked. This pattern can result in loss of data if the owner is re-rendered before the current value are submitted.
Generally, when using React, you want to avoid using native DOM methods to access or manipulate elements. (There are plenty of exceptions, of course.) One reason you want to avoid native DOM methods is that component lifecycle methods might execute before the React renderer has actually updated the DOM -- so document.getElementById('someid') might return undefined.

Correct way to access value of an input initialized with a prop when button is clicked in react

I have a react component that consists of an input field and a button. When the button is clicked I want to run an update function that is also inherited from the parent controller. In the react documentation they have an onchange handler attached to this input and they get the new value of the input with the onchange event object. However in my case I get an event object describing the button, not the input field. What is the correct way to access the new input field value from handle click?
class QuoteButton extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
this.props.onQuoteUpdate(//what should go here?)
}
render() {
const cost = this.props.cost;
return (
<div>
<Input value={cost}/>
<Button basic color='green' onClick={this.handleClick}>Submit Quote</Button>
</div>
);
}
}
If you a going to change input value in this component you need to use react states (if you don't use state management libraries such as mobx or redux). In most cases input have to be a controlled component.
After component was mounted add cost value to states. You also need appropriate handler for input.
P.S. You could use arrow functions to avoid binding handlers in constructor.
class QuoteButton extends React.Component {
constructor(props) {
super(props);
this.state = { inputValue: '' };
}
componentDidMount() {
this.setState({inputValue: this.props.cost});
}
handleClick = () => {
this.props.onQuoteUpdate(this.state.inputValue);
}
handleInputChange = event => {
this.setState({inputValue: event.target.value});
}
render() {
return (
<div>
<Input value={this.state.inputValue} onChange={this.handleInputChange} />
<Button basic color='green' onClick={this.handleClick}>Submit Quote</Button>
</div>
);
}
}
Hope it helps

Checkbox onCheck doesn't fire

I have a checkbox from material-ui that doesn't fire onCheck event.
<Checkbox
label="label"
onCheck={onCheck}
checked={currentDocument.ispublic}
/>
function onCheck() {
currentDocument.ispublic = !currentDocument.ispublic;
console.log("onCheck");
}
I tried removing checked property as described in this question: React Checkbox not sending onChange
currentDocument is an observable object stored in my store.
Can it be because based on https://material-ui-next.com/api/checkbox/#checkbox material-ui is exposing onChange not onCheck?
Without the full content, there is no way to test your code.
But there are some possible errors:
Where do onCheck and currentDocument come from? If it's in the component class, you need put this keyword.
Use arrow function for onCheck, otherwise, you can't access this without .bind(this)
The component class should have decorator #observer and currentDocument should have #observable
The code below is one possible answer, but I'm not sure what behavior you expected, so I didn't test. However, with MobX, the syntax is correct.
#observer
class MyCheckBox extends React.Component {
#observable currentDocument = {
ispublic: false,
};
render() {
return(
<Checkbox
label="label"
onCheck={this.onCheck}
checked={this.currentDocument.ispublic}
/>
);
}
onCheck = () => {
this.currentDocument.ispublic = !this.currentDocument.ispublic;
}
}

Get Input Value From Stateless Component

CONTEXT
I'm trying to get the value of an input field from a stateless component inside another stateless component and then use it to call a method. I'm using rebass for my UI component and doing this in Meteor + Mantra.
I understand that I could do this by using refs if I were using <input> HTML fields and not another stateless component.
PROBLEM
My current code yield preventDefault of undefined, and when removed, the console.log prints out each time the input changes, not on submit. I believe that my state applies to the entire Dashboard component, instead of the stateless Rebass <Input/>, but I do not know how to change this.
import React from 'react';
import {PageHeader, Container, Input,Button} from 'rebass';
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = { websiteUrl: ''};
this.onInputChange = this.onInputChange.bind(this);
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onInputChange(event) {
this.setState({websiteUrl:event.target.value});
}
onFormSubmit() {
event.preventDefault;
const {create} = this.props;
const {websiteUrl} = this.state.websiteUrl;
console.log(this.state.websiteUrl);
create(websiteUrl);
}
render() {
const { error } = this.props;
return (
<div>
<PageHeader
description="Dashboard Page"
heading="Dashboard"
/>
<Container>
<form>
<Input
value={this.state.websiteUrl}
type="text"
buttonLabel="Add Website"
label="Website"
name="add_website"
onChange={this.onInputChange}
/>
<Button
backgroundColor="primary"
color="white"
inverted={true}
rounded={true}
onClick={this.onFormSubmit()}
> Add Website </Button>
</form>
</Container>
</div>
);
}
}
export default Dashboard;
You should pass an event to the onFormSubmit function:
<Button
backgroundColor="primary"
color="white"
inverted={true}
rounded={true}
onClick={(event) => this.onFormSubmit(event)}
...

Categories