Reputation: 1016
I am trying to create a TextAreaAutosize component which returns a textarea which adjust its height automatically when need to.
I wanted to run onHeightChange function when height changes so i used svelte reactive statements($:) so that whenever change in height, that piece of code run.
App.svelte
<script>
import TextArea from "./TextAreaAutosize.svelte";
let val, ref;
</script>
<TextArea
bind:value={val}
bind:ref={ref}
style={"height: 90px; maxHeight=40"}
minRows={4}
/>
TextAreaAutosize.svelte
<script context="module">
import calculateNodeHeight from './calculateNodeHeight.js'
let uid = 0;
const noop = () => {};
</script>
<script>
export let value = '';
export let style;
export let useCacheForDOMMeasurements = false;
export let minRows = -Infinity;
export let maxRows = +Infinity;
export let onHeightChange = noop;
export let ref = null;
let refConfig = {
_uid: uid++
}
let state = {
height: 0,
minHeight: -Infinity,
maxHeight: +Infinity
}
// runs when change in height
$: if(state.height){
//block 1
console.log('height changed')
onHeightChange(refConfig)
}
// resizes element on initial mount or whenever change in value
$:if(ref || value){
//block 2
_resizeComponent()
}
// function for resizing textarea when required
function _resizeComponent(){
if(!ref){
return;
}
const refHeight = calculateNodeHeight(
ref,
refConfig._uid,
useCacheForDOMMeasurements,
minRows,
maxRows
);
const {
height,
minHeight,
maxHeight,
rowCount,
valueRowCount
} = refHeight;
refConfig.rowCount = rowCount;
refConfig.valueRowCount = rowCount;
if(
state.height !== height ||
state.maxHeight !== maxHeight ||
state.minHeight !== minHeight
){
state = {height, minHeight, maxHeight}
}
}
</script>
<textarea
bind:value={value}
bind:this={ref}
style={`${style};height: ${state.height}px;`}
on:click
on:change
class={$$props.class}
/>
In this component the block 1 should run whenever the state.height changes. but it doesn't run at all. But when i place it after the block2 it runs whenever there is change in state.height
when value of textarea change block2 run and calls _resizeComponent which then update state with new height if needed. which should trigger block1 if height change. but it triggers that block only if i place it after block2
i am not able to understand why this behaviour in my code. Statements in block1 should run whenever change in state.height irrespective of order of reactive statements
Upvotes: 2
Views: 1309
Reputation: 29615
Svelte can't determine, from analysing that code, that the second block could affect the value of state
. So it doesn't reorder the blocks to run the second one first. (It can't just keep re-running the reactive blocks if there are unexpected changes at the end; that way lies infinite loops.)
If instead of just _resizeComponent()
it was state = _resizeComponent()
, and the function was changed accordingly, that would change. Alternatively, just reorder the blocks manually.
Incidentally, there's an easier way to get an auto-resizing textarea: https://svelte.dev/repl/40f4c7846e6f4052927ff5f9c5271b66?version=3.6.8
Upvotes: 3