I need to add an input box (input-0, input-1...) each time a button is clicked.Following is the relevant code.
// State
this.state = {
newText: {}
};
// Code to add dynamic input text
addInputText = () => {
let dynamicTextsNo = Object.keys(this.state.newText).length;
let newInputId = dynamicTextsNo+1;
let dynamicTextArr = this.state.newText;
let newTextId = 'input-'+dynamicTextsNo;
dynamicTextArr[newTextId] = '';
let newState = { ...this.state, newText: dynamicTextArr }
this.setState( newState );
}
// Code to render dynamic input text.
dynamicTextArea = () => {
return Object.keys(this.state.newText).map( ( key ) => {
return ( <InputGroup key={key} borderType='underline'>
<Input placeholder='Type your text here' value={this.state.newText[key]} onChange={this.changeInput}/>
</InputGroup>
);
});
}
// Render function
render() {
return <View>{this.dynamicTextArea()}</View>
}
// handle input
changeInput = (e) => {
console.log( e.target.value ); // **this comes out to be undefined.**
}
Why is e.target.value in changeInput function undefined?
P.S. Jsfiddle link of the full code: https://jsfiddle.net/johnnash03/9by9qyct/1/
Unlike with the browser text input element, the event argument passed to React Native TextInput.onChange callback does not have a property called target.
Instead, use
<TextInput
onChange={(event) => event.nativeEvent.text}
/>
or
<TextInput
onChangeText={(text) => text}
/>
You must use bind like so <Input placeholder='Type your text here' value={this.state.newText[key]} onChange={this.changeInput.bind(this)}/>
Related
This is the render method, how i am calling the handler and setting the reactstrap checkbox.
this.state.dishes.map(
(dish, i) => (
<div key={i}>
<CustomInput
type="checkbox"
id={i}
label={<strong>Dish Ready</strong>}
value={dish.ready}
checked={dish.ready}
onClick={e => this.onDishReady(i, e)}
/>
</div>))
The handler for the onClick listener, I've tried with onchange as well but it apears that onchange doesnt do anything, idk why?
onDishReady = (id, e) => {
console.log(e.target.value)
var tempArray = this.state.dishes.map((dish, i) => {
if (i === id) {
var temp = dish;
temp.ready = !e.target.value
return temp
}
else {
return dish
}
})
console.log(tempArray)
this.setState({
dishes: tempArray
});
}
The event.target.value isn't the "toggled" value of an input checkbox, but rather event.target.checked is.
onDishReady = index => e => {
const { checked } = e.target;
this.setState(prevState => {
const newDishes = [...prevState.dishes]; // spread in previous state
newDishes[index].ready = checked; // update index
return { dishes: newDishes };
});
};
The rendered CustomInput reduces to
<CustomInput
checked={dish.ready}
id={i}
label={<strong>DishReady</strong>}
onChange={this.onDishReady(i)}
type="checkbox"
/>
No need to pass in a value prop since it never changes.
Note: Although an onClick handler does appear to work, semantically it isn't quite the correct event, you want the logic to respond to the checked value of the checkbox changing versus a user clicking on its element.
You can do it this way:
this.setState(function (state) {
const dishes = [...state.dishes];
dishes[id].ready = !e.target.value;
return dishes;
});
I am trying to send the contents of the text box to be displayed in the spam when I click on the button
class Hello extends Component {
state = {
texto: ""
}
changeText = () =>{
this.setState({texto: this.state.texto})
}
render() {
return (
<div>
<input type = "text" defaultValue = {this.state.texto}></input>
<p>{this.state.texto}</p>
<button onClick = {this.changeText}>Click</button>
</div>
);
}
}
export default Hello;
function App() {
return (
<div>
<Hello />
</div>
);
}
The idea is that when you click on the button it shows the ones in the text box inside the span.
I am new to react and this clarifies some doubts about the concepts.
thanks guys.
Carlos!
First of all, you must update your input so your code can work properly. In your input, do this instead:
<input type="text" value={this.state.texto} onChange={() => this.onChangeHandler(e.target.value)}></input>
Then, inside your class, you create an onChangeHandler to deal with the data from input first:
onChangeHandler = (e) => {
this.setState({
texto: e
});
}
Ok, we're almost there, now you must create another state item, so you can use it for your final input:
state = {
texto: "",
textoFinished: ""
}
Then, correct your onChange envent:
changeText = () =>{
this.setState({textoFinished: this.state.texto})
}
Now, to access the new value, just go to this.state.textoFinished. Happy coding!
You need to update the state using target value after clicking on it.
Something Like this :
state = {
texto: ""
}
onChangeHandler = (e) => {
const value = e.target.value;
this.setState({texto: value});
}
OR
onChangeHandler = (e) => {
const texto = e.target.value;
this.setState({texto});
}
Usually my forms are super long, I would like to use them as components in separate files, I tried doing so but I can no longer modify the values, I mean there's two steps in this config,
first I need to pass initial values from the API fetch request, I used props like demonstrated below :
// in parent
<InvoiceDetails {...this.state} />
// in component
...
render() {
let { invoicenumber, ponumber, invoicedate, paymentdue, loading } = this.props;
return (
<div>
{!loading ?
<Form>
<Input fluid value={invoicenumber}
type="text" onChange={this.handleForm}
placeholder="Invoice number" name="invoicenumberEdit" />
<DateInput
autoComplete="off"
name="invoicedate"
placeholder="Invoice date"
dateFormat='MMMM Do YYYY'
value={invoicedate}
clearable icon={false}
clearIcon={<Icon name="remove" color="black" />}
onChange={this.handleInvoiceDate}
/>
...
The functions that update those inputs are changing the parent state, so I couldn't move them to the component file because that would be two separate states.
handleInvoiceDate = (event, { name, value }) => {
if (this.state.hasOwnProperty(name)) {
this.setState({ [name]: value });
}
}
handleForm = e => {
this.setState({ [e.target.name]: e.target.value });
};
I don't use context, redux or anything like that. How can I solve this if possible?
In your parent, make your handler a pure set state like this
handleInputChange = (name, value) => {
this.setState({ [name]: value });
};
then pass your handler as props like this
<InvoiceDetails {...this.state} handleInputChange={this.handleInputChange} />
then in your component, add these functions to your code
handleInvoiceDate = (event, { name, value }) => {
if (this.state.hasOwnProperty(name)) {
this.props.handleInputChange(name, value);
}
}
handleForm = e => {
this.props.handleInputChange(e.target.name, e.target.value);
};
Just pass your function's from parent to child component as a props like,
<InvoiceDetails {...this.state} handleForm={this.handleForm} handleInvoiceDate={this.handleInvoiceDate}/>
Function's in parent,
handleInvoiceDate = (name, value ) => {
if (this.state.hasOwnProperty(name)) {
this.setState({ [name]: value });
}
}
handleForm = (name, value) => {
this.setState({ [name]: value });
};
In your component call these function's like,
<Input fluid value={invoicenumber}
type="text" onChange={(e)=>this.props.handleForm(e.target.name,e.target.value)}
placeholder="Invoice number" name="invoicenumberEdit"
/>
<DateInput
...
onChange={(e)=>this.props.handleInvoiceDate(e.target.name,e.target.value)}
/>
Fairly new to coding, and especially to React. Have seen other similar questions but can't seem to apply the answers to my code.
I'm trying to update the 'validate' state when the input length reaches 5 and I get the 'maximum update depth exceeded' error. From what I understand when the length reaches 5 it re-renders the DOM, finds that the length = 5 and so begins to call itself recursively (correct me if I'm wrong!), and so I'm trying to work out how to execute validationHandler() when the number reaches 5 only once.
class App extends Component {
state = {
inputLength: '0',
validate: 'Too short'
}
changeHandler = (event) => {
this.setState({ inputLength: event.target.value.length });
};
validationHandler = () => {
if (this.state.inputLength > 4) {
this.setState({ validate: "Enough"})
}
};
render() {
return (
<div className="App">
<input
type="text"
onChange={this.changeHandler.bind(this)}
/>
<Validation
change={this.validationHandler()}
validate={this.state.validate}/>
</div>
);
}
}
I have a separate validation component.
const validation = (props) => {
return (
<div className="validation">
<p onChange={props.change}>{props.validate}</p>
</div>
)
};
Thanks in advance!
You have exactly the same issue as ReactJS: Maximum update depth exceeded error.
However, while applying the solution, i.e.
<Validation
change={this.validationHandler}
validate={this.state.validate}/>
will fix the error, it won't make your app work. The validationHandler method will never be called because p elements do not trigger a change event.
<p onChange={props.change}>{props.validate}</p>
Instead you want to validate the input length whenever the input changes, so that should happen inside the changeHandler method:
changeHandler = (event) => {
this.setState({
inputLength: event.target.value.length, // remove if not needed
validate: event.target.value.length > 4 ? "Enough" : "Too short",
});
};
and
render() {
return (
<div className="App">
<input
type="text"
onChange={this.changeHandler}
/>
<Validation validate={this.state.validate}/>
</div>
);
}
Calling .bind(this) has no effect because this.changeHandler is an arrow function.
Change the code to below
class App extends Component {
state = {
validate: 'Too short'
}
changeHandler = (event) => {
if (event.target.value.length > 4) {
this.setState({ validate: "Enough"})
} else {
this.setState({ validate: "Too short"})
}
};
render() {
return (
<div className="App">
<input
type="text"
onChange={this.changeHandler.bind(this)}
/>
<Validation
validate={this.state.validate}/>
</div>
);
}
}
and component to
const validation = (props) => {
return (
<div className="validation">
<p>{props.validate}</p>
</div>
)
};
I have a form with a single textarea. When text is entered into this textarea a new textarea should be displayed under the current one. If this new textarea has text entered then again another new one shows underneath (and on and on...).
In order to prevent a new textarea being added every time text is entered (for example if there are 3 textareas and the user focuses and changes the text in the first) I am storing the activeBulletPointId in my state, and when text is entered in it I am checking to see if it is the last bullet point in the array.
addNewBulletToEnd = () => {
let lastBulletId = this.state.data.slice(-1);
lastBulletId = lastBulletId[0].id;
if (this.state.activeBulletPointId === lastBulletId) {
const newBulletPoint = { id: this.generateId(), title: 'Click to add' };
this.setState({ data: this.state.data.concat(newBulletPoint) });
}
}
The issue I have is that when rendering my list I am unsure how to pass the id to the onFocus function.
handleFocus = (e) => {
console.log(e); //Can I get the ID here?
if (this.state.activeBulletPointId !== selectedBulletPointId) {
this.setState({ activeBulletPointId: selectedBulletPointId });
}
}
render() {
const bulletList = this.state.data.map((bulletPoint) => {
const reduxFormName = `${this.props.placeholder}-${bulletPoint.id}`;
return (
<div key={bulletPoint.id} className="bullet-point-input">
<SelectInputType
placeholder={reduxFormName}
type="textarea"
onChange={this.updateText}
onFocus={this.handleFocus}
handleKeyPress={this.handleKeyPress(reduxFormName)}
handleKeyDown={this.handleKeyDown}
noLabel
/>
</div>
);
});
return (
<div className="bullet-point-list">
{bulletList}
</div>
);
}
The <SelectInputType> component is what renders my redux-form <Field> component.
You could create a handler for each field. So you would avoid keeping data in DOM (as attributes) and keep it in handler's scope.
Unless you have hundreds of fields this wont hit overall performance.
setActiveBullet = activeBulletPointId => {
if (this.state.activeBulletPointId !== activeBulletPointId ) {
this.setState({ activeBulletPointId });
}
}
render() {
const bulletList = this.state.data.map((bulletPoint) => {
const reduxFormName = `${this.props.placeholder}-${bulletPoint.id}`;
return (
<div key={bulletPoint.id} className="bullet-point-input">
<SelectInputType
placeholder={reduxFormName}
type="textarea"
onChange={this.updateText}
onFocus={() => this.setActiveBullet(bulletPoint.id)}
handleKeyPress={this.handleKeyPress(reduxFormName)}
handleKeyDown={this.handleKeyDown}
noLabel
/>
</div>
);
});
return (
<div className="bullet-point-list">
{bulletList}
</div>
);
}