Reputation: 1405
I am working on a project which is basically notepad. I am having problems though updating the <textarea>'s value when an ajax call is made. I tried setting the textarea's value property but then no changes to its value can be made. How can I make it so on a state change the textarea's value changes and can be edited.
The code I have is as follows.
In the parent class
<Editor name={this.state.fileData} />
In the Editor class
var Editor = React.createClass({
render: function() {
return (
<form id="noter-save-form" method="POST">
<textarea id="noter-text-area" name="textarea" value={this.props.name}></textarea>
<input type="submit" value="Save" />
</form>
);
}
});
I can't use defaultValue because the value of the textarea is not known on page load and when I try and put the data between the textareas nothing happens. I would like it to take the state value whenever the state changes but have it editable in between.
Thanks
Edit
I managed to get it working using jQuery but would like to do it in React instead, I called this before render:
$('#noter-text-area').val(this.props.name);
Upvotes: 72
Views: 193169
Reputation: 31
I've recently refactored an app that used the typical value={state} onChange={e => setState(e.target.value)}
for my textarea
To now using a useRef
hook and removing the value
and onChange
properties altogether. I update the ref with a custom setter function that updates ref.current.value
when I need to update programmatically, but otherwise simply let the user update the textarea value with default html functionality.
I may be re-inventing the wheel a bit, but I expose that ref through React context, and did not want every keystroke to cause re-renders across the app wherever that same context is consumed.
Upvotes: 0
Reputation: 270
IMHO all the "textarea content as state" non-sense is not needed - not sure, why react handles it different than the input
elements. Anyway, usually one wants to set the content of the textarea if an action occurred, which triggers a re-render of the page/textarea anyway. So the solution I use is to set the defaultValue
to a function, which set the textarea content.
E.g. I have an input element with a datalist which is backed by an array of json objects. If a user selects an option from the datalist, the idx of the selected option gets set to the state variable selectedIdx. This in turn causes a re-render of the page. When the re-render finally happens, the defaultValue of the textarea gets re-calculated (because it is an expression/function call) and this in turn gets the corresponding datalist[selectedIdx] object and sets the value of the textarea element to the desired value:
export default function PageFragment(): JSX.Element {
// ...
const [selectIdx, setSelectIdx] = useState(-1);
function onInput(e: React.CompositionEvent<HTMLInputElement>) {
e.preventDefault();
var ev = e.nativeEvent;
if (!(('inputType' in ev) && (ev.inputType === 'insertReplacementText')))
return;
var idx = getIdx(groups, ev.data.trim());
if (idx >= 0)
setSelectIdx(idx);
}
useEffect(() => {
// fetch groups and set selectedIdx if there is only one or pre-selected
}
function updateTextarea() {
var s = selectIdx >= 0 ? groups[selectIdx].desc : ''
var el = document.getElementById('desc') as HTMLInputElement;
if (el)
el.value = s;
return s;
}
// ...
return (
<div id='group'>
<div id="kform">
<h1>Gruppenverwaltung</h1>
...
<form action={ACTION_URL} method="post" onSubmit={handleSubmit} acceptCharset='utf-8'>
<section id='group-selection'>
<div className="kinput">
<label htmlFor="group">Gruppe</label>
<input id="group-selector" name="group" type="search" list={dlID}
onInput={onInput} />
</div>
{createDatalist(dlID, groups)}
</section>
<section id='update_group'>
<div className="kinput">
...
<label htmlFor="desc">Bemerkung</label>
<textarea id="desc" name="desc" defaultValue={updateTextarea()}/>
...
</div>
</section>
<div className="buttons">
<button type="submit">Gruppe aktualisieren</button>
</div>
</form>
</div>
</div>
);
}
So no onChange
handler is needed at all - it is absolutely useless, would be just overhead.
Upvotes: -2
Reputation: 32043
I think you want something along the line of:
Parent:
<Editor name={this.state.fileData} />
Editor:
var Editor = React.createClass({
displayName: 'Editor',
propTypes: {
name: React.PropTypes.string.isRequired
},
getInitialState: function() {
return {
value: this.props.name
};
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
render: function() {
return (
<form id="noter-save-form" method="POST">
<textarea id="noter-text-area" name="textarea" value={this.state.value} onChange={this.handleChange} />
<input type="submit" value="Save" />
</form>
);
}
});
This is basically a direct copy of the example provided on https://facebook.github.io/react/docs/forms.html
Update for React 16.8:
import React, { useState } from 'react';
const Editor = (props) => {
const [value, setValue] = useState(props.name);
const handleChange = (event) => {
setValue(event.target.value);
};
return (
<form id="noter-save-form" method="POST">
<textarea id="noter-text-area" name="textarea" value={value} onChange={handleChange} />
<input type="submit" value="Save" />
</form>
);
}
Editor.propTypes = {
name: PropTypes.string.isRequired
};
Upvotes: 94
Reputation: 7649
As a newbie in React world, I came across a similar issues where I could not edit
the textarea and struggled
with binding. It's worth knowing about controlled
and uncontrolled
elements when it comes to react.
The value of the following uncontrolled textarea
cannot be changed because of value
<textarea type="text" value="some value"
onChange={(event) => this.handleOnChange(event)}></textarea>
The value of the following uncontrolled textarea
can be changed because of use of defaultValue
or no value attribute
<textarea type="text" defaultValue="sample"
onChange={(event) => this.handleOnChange(event)}></textarea>
<textarea type="text"
onChange={(event) => this.handleOnChange(event)}></textarea>
The value of the following controlled textarea
can be changed because of how
value is mapped to a state as well as the onChange
event listener
<textarea value={this.state.textareaValue}
onChange={(event) => this.handleOnChange(event)}></textarea>
Here is my solution using different syntax. I prefer the auto-bind
than manual binding however, if I were to not use {(event) => this.onXXXX(event)}
then that would cause the content of textarea
to be not editable OR the event.preventDefault()
does not work as expected. Still a lot to learn I suppose.
class Editor extends React.Component {
constructor(props) {
super(props)
this.state = {
textareaValue: ''
}
}
handleOnChange(event) {
this.setState({
textareaValue: event.target.value
})
}
handleOnSubmit(event) {
event.preventDefault();
this.setState({
textareaValue: this.state.textareaValue + ' [Saved on ' + (new Date()).toLocaleString() + ']'
})
}
render() {
return <div>
<form onSubmit={(event) => this.handleOnSubmit(event)}>
<textarea rows={10} cols={30} value={this.state.textareaValue}
onChange={(event) => this.handleOnChange(event)}></textarea>
<br/>
<input type="submit" value="Save"/>
</form>
</div>
}
}
ReactDOM.render(<Editor />, document.getElementById("content"));
The versions of libraries are
"babel-cli": "6.24.1",
"babel-preset-react": "6.24.1"
"React & ReactDOM v15.5.4"
Upvotes: 15