Reputation: 23
I'm working on a markdown previewer using React (beginner) and I'm trying to display the current value of a textarea in a separate div on the page. I'm not sure how best to work with the textarea value using React.
My component hierarchy is as follows:
Currently I have a function in my App component that handles change to the textarea - this is passed down as props to the Editor component and further on to the Textarea component where it is called after the onChange event:
function App() {
const handleTextChange = () => {
const textarea = document.getElementById("textarea");
let text;
if (textarea === null) {
text = "";
} else {
text = textarea.value;
}
console.log(text);
return text;
};
return (
<div className="App">
<Header />
<div className="App-body">
<Editor handleTextChange={handleTextChange} />
<Preview />
</div>
</div>
);
}
export default App;
This function is working - I can see the textarea value updating in the console as I type.
Questions are as follows:
New to React (and Stack Overflow) - any advice (or links to related questions) appreciated
Upvotes: 2
Views: 1811
Reputation: 31
First you have two type of components: ES6 class component (which can use state through this.state) and in your case functional component. The functional component can't use the React state but a new feature was introduced in React which is called as react hooks (useState hook). In your case, you need to use a state variable in your app component and then pass this state to your components as through props. You need to set the state of your component and in your handleText change function and pass it to your preview component as prop.
Upvotes: 1
Reputation: 1075467
How should I pass this value to the desination div? Does this need to involve State, so that the destination div re-renders every time the textarea value changes?
That would be the usual way, yes.
Was declaring the handleTextChange function within my App component the right approach to begin with? Is there are better way to achieve this result?
Having handleTextChange
there is fine, but the problem is how you're accessing the textarea. In general, you shouldn't be going to the DOM for that. Instead, make the textarea
a controlled component (so its value is kept in state) and use that state both when rendering the textarea
and when rendering the preview div
.
Here's a really basic example:
const {useState} = React;
// The `Editor` component receives the value and the change function as
// props. I've called the change function `handleTextChange` so you can
// see how it relates to your code, but I might well have called it
// `setValue` otherWise.
const Editor = React.memo(({value, handleTextChange}) => {
// Our change handler just extracts the value from the textarea
// and passes it on. No need to memoize it here, probably,
// but if you wanted to you could use `useCallback` with the
// dependency array [handleTextChange].
const onChange = e => handleTextChange(e.target.value)
return (
<textarea
value={value}
onChange={e => handleTextChange(e.target.value)}
/>
);
});
// The Preview component shows the text
const Preview = ({text}) => {
return <div>{text}</div>;
};
// Just a stand-in
const Header = () => <div>(Header)</div>;
function App() {
// The state for the text and its setter
const [text, setText] = useState("");
// Notice how we can pass the state setter directly to `Editor`
// as `handleTextChange` below.
return (
<div className="App">
<Header />
<div className="App-body">
<Editor handleTextChange={setText} />
<Preview text={text} />
</div>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
Upvotes: 2