I want to make a 'number' component which accepts numbers on input.
I tried to make it, but it is not working.
Here the code-
import React, { Component } from 'react';
import { TextInput } from 'react-native';
constructor(props)
{
super(props);
this.state = {
text: ''
};}
handleInputChange = (text) => {
if (/^\d+$/.test(text)) {
this.setState({
text: text
});
}
}
const NumberInput = (props) => {
return (
<TextInput
keyboardType='numeric'
onChangeText={this.handleInputChange}
value={this.state.text}
/>
)
}
export { NumberInput };
You don't have access to this in functional component, you need to define it as class based component,
class NumberInput extends Component{
constructor(props){
super(props);
this.state = {
text: ''
};
}
handleInputChange = (text) => {
if (/^\d+$/.test(text)) {
this.setState({
text: text
});
}
}
render(){
return (
<TextInput
keyboardType='numeric'
onChangeText={this.handleInputChange}
value={this.state.text}
/>
)
}
}
Update
You can also try this,
<TextInput
keyboardType='numeric'
onChange={this.handleInputChange} //onChange instead of onChangeText
value={this.state.text}
/>
And your function should be,
handleInputChange = (e) => {
if (/^\d+$/.test(e.target.value)) {
this.setState({
text: e.target.value
});
}
}
Reference to this change.
Also, you can use Number() function to check if the input is a number. It not, it will return NaN
you should use the class component when to use the constructor and use super or use and used hock function with useState in react
class NumberInput extends Component{
constructor(props){
super(props);
this.state = {
text: ''
};
}
handleInputChange = (text) => {
if (/^\d+$/.test(text)) {
this.setState({
text: text
});
}
}
render(){
return (
<TextInput
keyboardType='numeric'
onChangeText={this.handleInputChange}
value={this.state.text}
/>
)
}
}
or using the following shape when used the function component
import useState from'react'
function NumberInput (){
const [text, setText] = useState('');
handleInputChange = (text) => {
if (/^\d+$/.test(text))(setText(text)) ;
}
}
return (
<TextInput
keyboardType='numeric'
onChangeText={this.handleInputChange}
value={text}
/>
)
}
}
Related
I want to access the state of a Child component by using refs, but the state of the ref is always null.
In my React app, I have an Editor(basically, it is a form) that manipulates its own states, e.g. value change, update. The editor is used on multiple pages.
Editor.jsx
export default class Editor extends React.Component {
constructor(props) {
super(props);
this.state = {
value1: null,
... other values
};
}
onValue1Change = (e) => {
this.setState({value1: e.target.value});
}
onSave = (e) => {
// save values
}
render() {
return (
<div>
<input value={this.state.value1} onChange={this.onValue1Change}/>
... other input fields
<button onClick={this.onSave}>Save</button>
</div>
)
}
}
Now, there is a RegisterForm which covers all fields in the Editor. I made a small change in the Editor to hide the Save button so I can use it in the RegisterForm:
RegisterForm.jsx
export default class RegisterForm extends React.Component {
constructor(props) {
super(props);
this.state = {
email: null,
firstname: null,
lastname: null
};
this.Editor = React.createRef();
}
onSave = (e) => {
let childState = this.Editor.current.state;
// childState is ALWAYS null!
}
render() {
return (
<div>
<input value={this.state.email} onChange={this.onEmailChange}/>
<input value={this.state.firstname} onChange={this.onFirstnameChange}/>
<input value={this.state.lastname} onChange={this.onLastnameChange}/>
...
<Editor ref={this.Editor} showSave={false}/>
...
<button onClick={this.onSave}>Save</button>
</div>
)
}
}
Turns out this.Editor.current.state is always null.
I have two questions.
Why this.Editor.current.state is null?
If I want to use props, how should I change my code? E.g. If I let RegisterForm pass props to Editor, I'd imagine something like this:
Editor.jsx
export default class Editor extends React.Component {
// same constructor
onValue1Change = (e) => {
this.setState({value1: e.target.value}, () => {
if(this.props.onValue1Change) this.props.onValue1Change(e);
});
}
// same render
}
RegisterForm.jsx
export default class RegisterForm extends React.Component {
constructor(props) {
super(props);
this.state = {
email: null,
firstname: null,
lastname: null,
value1: null,
};
}
onValue1Change = (e) => {
this.setState({value1: e.target.value});
}
render() {
return (
<div>
<Editor showSave={false} onValue1Change={this.onValue1Change}/>
...
</div>
)
}
}
does it make the Child component render twice? Any suggestions on how to improve it?
You are passing the ref as a prop to the <Editor/> component but not doing anything with it after that.
For example:
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
Receive props and ref through the forwardRef() callback parameter, then pass the ref to the child node.
This is called ref forwarding
I made a code sandbox for you to test it!
Giving the components below, I am unable to type in TextInput the letter is written and then deleted, it seems a problem with updating the state. Any clue?
class myContainerComponent extends Component {
constructor(props) {
super(props)
this.onChange = this.onChange.bind(this)
}
onChange(value) {
this.setState({
value
})
}
render() {
return (
<PresentationalComponent
onChange={this.onChange}
value={this.state.value}
/>
)
}
}
class PresentationalComponent extends Component {
render() {
return(
<TextInput
onChangeText={this.props.onChange}
value={this.props.value}
/>
)
}
}
Any clue?
You should initialise your state in your constructor:
constructor(props){
super(props);
this.state = {
value: ''
}
}
Add this to your myContainerComponent constructor:
this.state = {
value: '',
}
I believe both other answers are correct but you could simplify your code even more and get rid of the constructor declaration using an arrow function:
class myContainerComponent extends Component {
state = {
value: ''
}
onChange = (value) => {
this.setState({
value
})
}
render() {
return (
<PresentationalComponent
onChange={this.onChange}
value={this.state.value}
/>
)
}
}
import * as React from 'react';
import { TextInput } from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
value: {}
};
this.onChange = this.onChange.bind(this)
}
onChange(value) {
this.setState({
value
})
}
render() {
return (
<PresentationalComponent
onChange={this.onChange}
value={this.state.value}
/>
)
}
}
class PresentationalComponent extends React.Component {
render() {
return(
<TextInput
onChangeText={this.props.onChange}
value={this.props.value}
/>
)}
}
I have a class of this form:
export default class FixedMem {
constructor(totalMem){
this._totalMem = totalMem
}
get totalMem(){
return this._totalMem
}
addMem(mem){
this._totalMem += mem
}
}
I import it into my react component like this :
import Fixed from '../somewhere'
If i want to create a new classes with varying parameters based on input from a textbox and display its values. How do i call its methods from inside the render method ?. This somewhat illustrates my problem
class fixedBlock extends Component {
constructor(){
super()
this.state = {
"textInput":"",
"totalMem":0,
"fixed":null
}
}
handleInputChanged(e){
this.setState({
"textInput":e.target.value
})
}
handleButtonPressed(){
this.setState({"fixed":new Fixed(parseInt(this.state.textInput))})
}
incrementButtonPressed(){
this.state.fixed.addMem(2)
}
render(){
return(
<div>
<input type="button" onClick={this.handleInputChanged} value=
{this.state.textInput}>
<button onClick={this.handleButtonPressed}>create</button>
<button onClick={this.incrementButtonPressed}> increment </button>
<p>{this.state.fixed.totalMem}</p>
</div>
)
}
}
this doesn't work, another approach i had to solve this problem was using closures, so inside my react component :
class fixedBlock extends Component{
constructor(){//stuff here}
FixedMem () {
var FixedObj = null
return {
initFixed: function (totalMem) {
FixedObj = new Fixed(totalMem, divisions)
},
totalMem: function () {
return FixedObj.totalMem
},
increment: function(){
FixedObj.addMem(2)
}
render(){//stuff here}
}
How do i even use this in the render method ?
There are several issues with your code example. Missing closing tags and rebinding of methods missing.
Here's an example of dynamically usage of a class instance in a React component. However I can not recommend to use this approach. This is mainly as proof of concept.
class MyValue {
constructor(val) {
this._val = parseInt(val, 10) || 0;
}
get total() {
return this._val;
}
set total(val) {
this.val = val;
}
add(val) {
this._val += val;
}
subtract(val) {
this._val -= val;
}
}
Here's the React component
class Block extends React.Component {
constructor() {
super();
this.state = {
textInput: "",
myValue: new MyValue()
};
}
handleInputChanged(e) {
this.setState({
textInput: e.target.value
});
}
handleButtonPressed() {
this.setState({ myValue: new MyValue(this.state.textInput) });
}
incrementButtonPressed() {
this.state.myValue.add(2);
this.forceUpdate(); /* React does not know the state has updated, force update */
}
render() {
return (
<div>
<input type="number" step="1" onChange={this.handleInputChanged.bind(this)} />
<button onClick={this.handleButtonPressed.bind(this)}>create</button>
<button onClick={this.incrementButtonPressed.bind(this)}>increment</button>
<p>{this.state.myValue.total}</p>
</div>
);
}
}
As an alternative approach. You could use a pattern where you separate logic from presentation. Here's an example using function as child. The Calculator handles the calculation and Presentation uses the calculator and present the GUI.
class Calculator extends React.Component {
constructor() {
super();
this.state = {value: 0};
}
add(value){
this.setState(prevState => ({value: prevState.value + value}));
}
subtract(value){
this.setState(prevState => ({value: prevState.value - value}));
}
set(){
this.setState(prevState => ({value: parseInt(prevState.input, 10) || 0}));
}
input(value){
this.setState({input: value});
}
render() {
return this.props.children(
{
value: this.state.value,
add: this.add.bind(this),
subtract: this.subtract.bind(this),
set: this.set.bind(this),
input: this.input.bind(this),
});
}
}
const Presentation = props => (
<Calculator>
{ ({value,add,subtract,set,input}) => (
<div>
<button onClick={() => add(2)}>add 2</button>
<button onClick={() => subtract(3)}>subtract 3</button>
<input type="number" step="1" onChange={e => input(e.target.value)} />
<button onClick={set}>set</button>
<p>{value}</p>
</div>)
}
</Calculator>);
The problem with the first attempt is that you are mutating a Component's state without letting React know about it. You need to use setState() or forceUpdate(). One way to still have FixedMem manage your state while letting React know could be:
class FixedBlock extends Component {
constructor(props) {
super(props);
this.state = {
textInput: '',
totalMem: 0
};
this.fixedMem = new FixedMem(0);
this.sync = this.sync.bind(this);
}
sync() {
const totalMem = this.fixedMem.totalMem;
this.setState({ totalMem });
}
handleInputChanged(evt) {
this.setState({ textInput: evt.target.value });
}
handleButtonPressed() {
this.fixedMem = new FixedMem(parseInt(this.state.textInput));
this.sync();
}
incrementButtonPressed() {
this.fixedMem.addMem(2);
this.sync();
}
render() {
return (
<div>
<input type="text" onChange={this.handleInputChanged.bind(this)} />
<button onClick={this.handleButtonPressed.bind(this)}>create</button>
<button onClick={this.incrementButtonPressed.bind(this)}>increment</button>
<p>{this.state.totalMem}</p>
</div>
);
}
}
I am trying to use redux-form with react-widget Multiselect this example:
var Multiselect = ReactWidgets.Multiselect
, people = listOfPeople();
var Example = React.createClass({
getInitialState() {
return { value: people.slice(0,2) };
},
_create(name){
var tag = { name, id: people.length + 1 }
var value = this.state.value.concat(tag)
// add new tag to the data list
people.push(tag)
//add new tag to the list of values
this.setState({ value })
},
render(){
// create a tag object
return (
<Multiselect data={people}
value={this.state.value}
textField="name"
onCreate={this._create}
onChange={value => this.setState({ value })}/>
)
}
});
ReactDOM.render(<Example/>, mountNode);
Below is a code snippet for a parent component which makes usage of redux-form (EditVideo component) component (please look at the comments in onSubmit method):
class VideoEdit extends React.Component {
constructor(props) {
super(props);
}
onSubmit = (values) => {
console.log(values.categories) // always returns initialValues for categories, new values not adding
}
render() {
const { loading, videoEdit, categories } = this.props;
if (loading) {
return (
<div>{ /* loading... */}</div>
);
} else {
return (
<div>
<h2>Edit: {videoEdit.title}</h2>
<EditVideo
onSubmit={this.onSubmit}
initialValues={videoEdit}
categories={categories}
/>
</div>
);
}
}
}
And here is a code snippet of redux-form component with react-widget Multiselect component:
class CategoryWidget extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.defValue,
extData: this.props.data
}
this._create = this._create.bind(this);
}
_create(name) {
var tag = { name, id: this.state.extData.length + 100 + 1 }
var value = this.state.value.concat(tag)
var extData = this.state.extData.concat(tag)
this.setState({
extData,
value
})
}
render() {
return (
<Multiselect
{...this.props.input}
data={this.state.extData}
onBlur={() => this.props.input.onBlur()}
value={this.state.value || []}
valueField="id"
textField="name"
onCreate={this._create}
onChange={value => this.setState({ value })}
/>
)
}
}
const EditVideoForm = (props) => {
const { handleSubmit, submitting, onSubmit, categories, initialValues, defBook } = props;
return (
<Form name="ytvideo" onSubmit={handleSubmit(onSubmit)}>
<div>
<Field
name="categories"
component={CategoryWidget}
data={categories}
defValue={initialValues.categories}
/>
</div>
<br />
<Button color="primary" type="submit" disabled={submitting}>
Submit
</Button>
</Form>
);
};
export default reduxForm({
form: 'videoEdit',
enableReinitialize: true
})(EditVideoForm);
The Multiselect widget works as expected, yet the form on submit always returns the same initial values for categories.
I believe the problem lays in the fact that CategoryWidget is a class base component? If so, what is a way to make it work?
Here is what I have done for my Multiselect at the end:
class CategoryWidget extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.defValue,
extData: this.props.data
}
this._create = this._create.bind(this);
}
_create(name) {
var tag = { name, id: this.state.extData.length + 100 + 1 }
var value = this.state.value.concat(tag)
var extData = this.state.extData.concat(tag)
this.setState({
extData,
value
})
}
componentDidUpdate() {
let { onChange } = this.props.input
onChange(this.state.value)
}
handleOnChange(value) {
this.setState({ value })
}
render() {
const input = this.props.input
return (
<Multiselect
{...input}
data={this.state.extData}
onBlur={() => input.onBlur()}
value={this.state.value || []}
valueField="id"
textField="name"
onCreate={this._create}
onChange={value => this.handleOnChange(value)}
/>
)
}
}
I want to setup the onChangeText function for a TextInput which is a child element of an array, but I get 'undefined is not a function (evaluating '_this2.props.updatePhoneNumber(text)')'
I'm not sure which part I've done wrong so I just paste my code here.
The child item part:
class PhoneInput extends React.Component {
render() {
return ( <FormGroup style={{flexDirection: 'row'}}>
<TextInput placeHolder="phone" value={this.props.phone.number}
onChangeText={(text) => {this.props.updatePhoneNumber(text)}}/>
</FormGroup>);
}
}
The father component:
export class RecommendScreen extends React.Component {
state = {
phones: [{number: "", id: 1}, {number: "", id: 2}]
}
constructor(props) {
super(props);
this.updatePhoneNumber = this.updatePhoneNumber.bind(this);
}
updatePhoneNumber(id, number) {
const phones = this.state.phones.slice();
for (let phone of phones) {
if (phone.id == id) {
phone.number = number;
}
}
this.setState({phones: phones});
}
render() {
return (
<Screen styleName="paper">
{this.state.phones.map((phone, i) => (
<PhoneInput phone={phone} key={phone.id}
onChangeNumber={(text) => {this.updatePhoneNumber(phone.id, text)}}
/>
))}
</Screen>
);
}
}
Any ideas?
In the first line, you just need to pass the function to the child component.
export class RecommendScreen extends React.Component {
state = {
phones: [
{
number: "",
id: 1
}, {
number: "",
id: 2
}
]
}
constructor(props) {
super(props);
this.updatePhoneNumber = this.updatePhoneNumber.bind(this);
}
updatePhoneNumber(id, number) {
const phones = this.state.phones.slice();
for (let phone of phones) {
if (phone.id == id) {
phone.number = number;
}
}
this.setState({phones: phones});
}
render() {
return (
<Screen styleName="paper">
{this.state.phones.map((phone, i) => (<PhoneInput
phone={phone}
key={phone.id}
updatePhoneNumber={this.updatePhoneNumber}/>))}
</Screen>
);
}
}
In this component, just call this function and pass the value to it
class PhoneInput extends React.Component {
render() {
return (
<FormGroup style={{
flexDirection: 'row'
}}>
<TextInput
placeHolder="phone"
value={this.props.phone.number}
onChange={(e) => {this.props.updatePhoneNumber(e.target.value)
}}/>
</FormGroup>
);
}
}
It's a name mismatch issue, You are passing the function by different name using by different name, From parent you are passing onChangeNumber and in child you are using updatePhoneNumber that's why it is throwing the error.
Use this:
<PhoneInput
phone={phone}
key={phone.id}
updatePhoneNumber = {(text) => this.updatePhoneNumber(phone.id, text)}}
/>
Or Inside child component use onChangeNumber like this:
<TextInput
placeHolder = "phone"
value = {this.props.phone.number}
onChangeText={(text) => this.props.onChangeNumber(text)}/>