Reputation: 13
The two text areas are only slightly different, and I can't understand why the second won't work .
const StyledTextarea = styled.textarea`
display: block;
font-size: 20px;
line-height: 40px;
min-height: 120px;
overflow: hidden;
padding: 0 7px;
margin: 0 0 30px;
resize: none;
width: 500px;
`;
const Label = styled.span`
color: ${props => props.green ? '#00BB00' : 'red'};
font-size: 1.5em;
`;
const App = () => {
return(
<div>
<Label green>✅ Textarea1 - Working </Label>
<Textarea1 />
<Label>❌ Textarea2 - Not working </Label>
<Textarea2 />
</div>
)
}
class Textarea1 extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Assumenda voluptas deleniti at, molestias in amet dolore voluptatem atque, modi minus ipsam dignissimos...',
scrollHeight: 0,
}
}
onChange(e) {
const t = e.target;
t.style.height = 'auto';
t.style.height = `${t.scrollHeight}px`;
this.setState({value: t.value});
}
render() {
return (
<StyledTextarea
value={this.state.value}
onChange={this.onChange.bind(this)}
/>
);
}
}
class Textarea2 extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Assumenda voluptas deleniti at, molestias in amet dolore voluptatem atque, modi minus ipsam dignissimos...',
scrollHeight: 0,
}
}
onChange(e) {
const bugger = e.target;
console.log('style.height before it is set to auto: ', bugger.style.height)
bugger.style.height = 'auto';
console.log('style.height after it is set to auto (obviously): ', bugger.style.height)
console.log('.scroll height: ', bugger.scrollHeight);
this.setState({
scrollHeight: bugger.scrollHeight,
value: bugger.value
});
}
render() {
return (
<StyledTextarea
style={{height: `${this.state.scrollHeight}px`}}
value={this.state.value}
onChange={this.onChange.bind(this)}
/>
);
}
}
Both use a similar way of keeping their height in sync with the height of the content. However, the second, which I thought should re-render with the new style.height
(coming from the updated state), doesn't apply the new style. Watch the console for the values in question - style.height stays 'auto' even after the assignment of the new value .
It works though if you only press Enter and Backspace .
What is it I don't understand?
Upvotes: 1
Views: 2399
Reputation: 1712
This is a tricky problem more related to your usage of the styled-components library with React than your React code itself. In the render of your Textarea1
you are creating a StyledTextarea
with a style prop passed in. Unfortunately this prop is ignored by components created with styled-components.
When using styled-components with react you are supposed to pull your props out and apply them to your css, as shown in this code-pen:
const styled = styled.default;
const Button = styled.button`
background: red;
border-radius: 8px;
color: white;
height: ${props => props.small ? 40 : 60}px;
width: ${props => props.small ? 60 : 120}px;
`;
class Application extends React.Component {
render() {
return (
<div>
<Button small>Click Me</Button>
<Button large>Click Me</Button>
</div>
)
}
}
ReactDOM.render(<Application />, document.getElementById('content'));
To fix your problem you will have to pull your style.height prop out in your StyledTextArea
and reset your height to ''
at the end of Textarea2.onChange. Here is your code with these changes. Note that this breaks the resizing done in Textarea1:
const styled = styled.default;
const StyledTextarea = styled.textarea`
display: block;
font-family: PT Sans;
font-size: 20px;
line-height: 40px;
min-height: 120px;
overflow: hidden;
padding: 0 7px;
margin: 0 0 30px;
resize: none;
width: 500px;
// one update here
height: ${props => (props.style && props.style.height) ? props.style.height + 'px' : '0px'};
`;
const Label = styled.span`
color: ${props => props.green ? '#00BB00' : 'red'};
font-size: 1.5em;
`;
const App = () => {
return(
<div>
<Label green>✅ Textarea1 - Working </Label>
<Textarea1 />
<Label>❌ Textarea2 - Not working </Label>
<Textarea2 />
</div>
)
}
class Textarea1 extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Assumenda voluptas deleniti at, molestias in amet dolore voluptatem atque, modi minus ipsam dignissimos...',
scrollHeight: 0,
}
}
onChange(e) {
const t = e.target;
t.style.height = 'auto';
t.style.height = `${t.scrollHeight}px`;
this.setState({value: t.value});
}
render() {
return (
<StyledTextarea
value={this.state.value}
onChange={this.onChange.bind(this)}
/>
);
}
}
class Textarea2 extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Assumenda voluptas deleniti at, molestias in amet dolore voluptatem atque, modi minus ipsam dignissimos...',
scrollHeight: 0,
}
}
onChange(e) {
const bugger = e.target;
console.log('style.height before it is set to auto: ', bugger.style.height)
bugger.style.height = 'auto';
console.log('style.height after it is set to auto (obviously): ', bugger.style.height)
console.log('.scroll height: ', bugger.scrollHeight);
this.setState({
scrollHeight: bugger.scrollHeight,
value: bugger.value
});
// another update here
bugger.style.height = '';
}
render() {
console.log(this.state.scrollHeight);
return (
<StyledTextarea
style={{height: this.state.scrollHeight}}
value={this.state.value}
onChange={this.onChange.bind(this)}
/>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
A final note, the second approach you are using is definitely the more preferable one! Modifying your components styles via a handler as opposed to the render method is not ideal.
Upvotes: 2