[Svelte]Svelte State & Props

Svelte.js State & Props

 - 본 글은 props의 state와 props 정의와 사용 방법에 대해 알아보겠습니다.
 - React, Vue와 차이점을 중심으로 기술하겠습니다.

1. 상태 정의

  • 상태 정의는 VanillaJs와 같이 변수 선언을 통해 상태를 정의합니다.
  • React와 Vue에 비해 보일러플레이트 코드가 적은것이 특징입니다.
  • React는 state에 관해 setter를 제공하며 이 setter를 통해 데이터를 변경해야 합니다.(이를 통해 리렌더링)
  • Vue는 data function 내부에 return 값이 state가 되며, React와 달리 이에 대한 직접 할당으로 데이터를 변경합니다.
  • Svelte의 경우 내부에 변수 및 함수를 정의하며 이를 직접 할당하며 데이터를 변경합니다.
  • Array, Object의 경우 주소값을 유지하며 내부 데이터를 조작할 경우 리렌더링이 되지 않습니다. 객체에 대한 재할당을 해야 Svelte가 이를 알아채고 화면을 다시 그립니다.
  • state 할당 방식은 Vue와 비슷하게 동작하는 듯 합니다.
// React
export default function Index() {
    const [name] = React.useState('React');
    const [inputValue, setInputValue] = React.useState("");
    const [list, setList] = React.useState(['item1', 'item2']);
    ....
}    
// Vue
<script>
export default {
  data: function () {
    return {
      name: 'Vue',
      inputValue: '',
      list: ['item1', 'item2']
    }
  }
}
</script>
// Svelte
<script>
	let name = 'Svelte';
	let inputValue = '';
	let list = ['item1', 'item2'];
</script>

2. method or function 정의.

  • 내부 function or method의 정의도 vanillaJs와 같이 선언형으로 합니다.
  • React의 경우 함수 작성의 경우 스코프내 useCallback, useMemo를 이용하거나, 일반 함수를 선언해 사용합니다.
  • Vue의 경우 methods 내부에 함수를 정의해 사용합니다.
  • Svelte의 경우 태그내에 함수를 선언하고 사용합니다.
// React
export default function Index() {
    ....
    const onClick = () => {
        const value = inputRef?.current?.value;
        setList(prevState => [...prevState, value]);
    }
    ....
// Vue
<script>
export default {
  ....
  methods: {
    onClick() {
      this.list.push(this.inputValue);
    }
  }
}
</script>
//Svelte
<script>
	....
	function onClick() {
		list = [...list, inputValue];
	}
</script>

3. Props 전달 방식.

  • 기존의 방식과 비슷하지만 간단 명료한 정의와 전달 방식을 갖고 있습니다.
  • 컴포넌트에 key={value} 형식으로 전달.
  • 자식 컴포넌에서는 export keyword를 통해 props임을 명명.
// React
// Index.jsx

import React from 'react'
import Item from "./Item";
export default function Index() {
    const [name] = React.useState('React');
    const [inputValue, setInputValue] = React.useState("");
    const [list, setList] = React.useState(['item1', 'item2']);
    const inputRef = React.useRef(null);
    const onClick = () => {
        const value = inputRef?.current?.value;

        setList(prevState => [...prevState, value]);
    }

    return (
        <main>
            <h1 style=>Hello {name}</h1>
            <p>Visit the {name}</p>
            <input ref={inputRef} type="text" placeholder="입력" value={inputValue} onChange={(e) => setInputValue(e.target.value)}/>
            <button onClick={onClick}>입력</button>
            <div>입력 값 : {inputValue}</div>
            {
                list?.length && list.map((item) => <Item text={item}/> )
            }
        </main>
    )

}

// Item.jsx
import React from 'react'

export default function Item({text}) {
    return (
        <li>{text}</li>
    )
}
// Vue

//Index.vue


<script>
import Item from "./Item";
export default {
  components: {Item},
  data: function () {
    return {
      name: 'Vue',
      inputValue: '',
      list: ['item1', 'item2']
    }
  },
  methods: {
    onClick() {
      this.list.push(this.inputValue);
    }
  }
}
</script>

<template>
  <main>
    <h1>Hello </h1>
    <p>Visit the !!.</p>
    <input type="text" placeholder="입력" v-model="inputValue">
    <button v-on:click="onClick">입력</button>
    <div>입력 값 : </div>

    <Item v-for="text in list" v-bind:text="text"/>

  </main>
</template>
// Svelte

// Index.svelte
<script>
	import Item from "./Item.svelte";

	let name = 'Svelte';
	let inputValue = '';
	let list = ['item1', 'item2'];
	function onClick() {
		list = [...list, inputValue];
	}
</script>

<main>
	<h1>Hello {name}!</h1>
	<p>Visit the Svelte!!</p>
	<input type="text" placeholder="입력" bind:value={inputValue}>
	<button on:click={onClick}>입력</button>
	<div>입력  : {inputValue}</div>
	{#each list as text}
		<Item text={text}/>
	{/each}
</main>

// Item.svelte
<script>
    export let text;
</script>

<li>{text}</li>

4. function props 전달

  • React : 변수 props와 마찬가지로 key=value 형식으로 전달
  • Vue: 변수 props와 마찬가지로 kye=value 형식으로 전달할 수도 있고, @eventName 형식으로 전달해 child component에서 $emit을 통해 호출할 수 있습니다.
  • Svelte: 변수 props와 마찬가지로 kye=value 형식으로 전달할 수도 있고, on:eventName 형식으로 전달해 child component에서 createEventDispatcher를 통해 dispatch할 수 있습니다.
//React

//Index.jsx
import React from 'react'
import FunctionPropItem from "./FunctionPropItem";
export default function Index() {
    const [name] = React.useState('React');
    
    const onAlert = (text, e) => {
        alert(`parameter : ${text}, event target value : ${e.target.value}`);
    }

    return (
        <main>
            <h1 style=>Hello {name}</h1>
            <p>Visit the {name}</p>
            <FunctionPropItem onAlert={onAlert}/>
        </main>
    )

}

//FunctionPropItem.jsx
import React from 'react'

export default function AlertPropItem ({onAlert}) {
    return (
        <button value="React" onClick={(e) => onAlert('React Alert', e)}>alert 호출</button>
    )
}
//Vue

//Index.vue
<script>
import FunctionPropItem from "./FunctionPropItem";
export default {
  components: {FunctionPropItem},
  data: function () {
    return {
      name: 'Vue',
    }
  },
  methods: {
    onAlert(text, e) {
      alert(`parameter : ${text}, event target value : ${e.target.value}`);
    },
    onAlert2(text, e) {
      alert(`parameter : ${text}, event target value : ${e.target.value}`);
    }
  }
}
</script>

<template>
  <main>
    <h1>Hello </h1>
<!--    <p @click="()=>console.log('click Vue')">Visit the !!.</p> Vue instance 외부 global에 접근할 수 없음.-->
    <p>Visit the !!.</p>
    <FunctionPropItem @onAlert="onAlert" v-bind:onAlert2="onAlert2"/>
  </main>
</template>

<style>
main {
  text-align: center;
  padding: 1em;
  max-width: 240px;
  margin: 0 auto;
}

h1 {
  color: #47C83E;
  text-transform: uppercase;
  font-size: 4em;
  font-weight: 100;
}
input {
  margin-bottom: 10px;
  margin-top: 20px;
}

@media (min-width: 640px) {
  main {
    max-width: none;
  }
}
</style>

//FunctionPropItem.vue
<template>
  <div>
    <button value="Vue" @click="$emit('onAlert', 'Vue Alert', $event)">alert 호출하기</button>
    <button value="Vue2" @click="(e)=>onAlert2('Vue2', e)">alert 호출하기2</button>
<!--    //<div @click="(e) => alert('aa')">text</div> vm에 alert가 없으므로 alert가 동작하지 않음.-->
  </div>
</template>

<script>
export default {
  name: "FunctionPropItem",
  props: {
    onAlert2: Function
  },

}
</script>
//Svelte

//Index.svelte
<script>
	import FunctionPropItem from "./FunctionPropItem.svelte";

	let name = 'Svelte';

	function onAlert(text, e) {
		alert(`parameter : ${text}, event target value : ${e.target.value}`);
	}

	function onAlert2(e) {
		alert(`parameter : ${e.detail.text}, event target value : ${e.detail.value}`);
	}
</script>

<main>
	<h1>Hello {name}!</h1>
	<p on:click={() => console.log("click Svelte")}>Visit the Svelte!!</p>
	<FunctionPropItem {onAlert} on:onAlert2={onAlert2}/>
</main>

<style>
	main {
		text-align: center;
		padding: 1em;
		max-width: 240px;
		margin: 0 auto;
	}

	h1 {
		color: #ff3e00;
		text-transform: uppercase;
		font-size: 4em;
		font-weight: 100;
	}
	input {
		margin-bottom: 10px;
		margin-top: 20px;
	}


	@media (min-width: 640px) {
		main {
			max-width: none;
		}
	}
</style>

//FunctionPropItem.svelte

<script>
    import {createEventDispatcher} from 'svelte'
    const dispatch = createEventDispatcher();
    export let onAlert

    function innerOnAlert () {
        dispatch('onAlert2', {
            text: 'Svelte2',
            value: 'Svelte Emit'
        })
    }
</script>

<button value="Svelte" on:click={(e) => onAlert('Svelte', e)}>alert 호출</button>
<button value="Svelte2" on:click={innerOnAlert}>alert 호출 2</button>
<button on:click={(e) => console.log("inner instance test")}>inner instance test</button>
<button on:click="{(e) => console.log('inner instance test2')}">inner instance test2</button>
git example :
 - https://github.com/JongHyuckLee/svelte-study