iT邦幫忙

2023 iThome 鐵人賽

DAY 13
0
Modern Web

從Vue學React!不只要會用,還要真的懂~系列 第 13

【Day 13】Vue與React實現互動效果的事件綁定

  • 分享至 

  • xImage
  •  

前幾天已經了解關於state的存取及更新方式,還有從父層把state透過props的傳遞給子層的方式,今天就讓我們把重點放在怎麼讓頁面能擁有實際的互動功能,也就是「觸發state更新有關的事件綁定,以及在單向資料流概念下,該如何在子層改動父層傳遞的state」的部分。

綁定事件監聽實現互動功能

看完props的部分和元件顯示相關設計的部分後,想讓頁面有實際上的互動效果,還必須綁定事件。不論是Vue還是React都需要透過綁定事件,來實現互動功能,差異只在於寫法上的不同。

vue:使用v-on語法綁定事件

<template>
  <div>
    <h2>Vue Event Binding</h2>
    <p>count: {{ count }}</p>
    <!-- 透過v-on(縮寫為@)綁定click事件 -->
    <button @click="handleAddCount">Add</button>
  </div>
</template>

<script setup>
import { ref } from 'vue';
const count = ref(0);
const handleAddCount = () => {
  count.value ++;
}
</script>

react:用on+事件名稱綁定事件

import { useState } from 'react';

function App() {
  const [count, setCount] = useState(0);
  const handleAddCount = () => {
    setCount(count + 1);
  };
  return (
    <div className="App">
      <h2>React Event Binding</h2>
      <p>count: {count}</p>
      {/* 用onClick綁定Click事件 */}
      <button onClick={handleAddCount}>Add</button>
    </div>
  );
}

export default App;

可以看到上述兩個範例雖然Vue和React在寫法上有點不太一樣,不過其實都是on+事件名稱的概念下去綁定event handler,所以其實在轉換上還算容易。緊接著來看一下另一個Vue和React在使用上比較不同的事件綁定情境!

子元件更新props的方式-透過事件觸發

前面提到的事件綁定,都是在自己的元件中,去改動自己元件中的state或是做一些動作,但是還有一種情境是「當我們想要從子元件改動父元件的props時」。如同之前在了解單向資料流時有提到過的「當畫面要改動state,需要靠事件進行」,當想從子元件往回改動父元件的state時,一樣也需要透過事件,但是在這裡的做法上,Vue和React就有明顯的不同,雖然本質還是「事件」為基準。

這裡一樣用一個相同的使用情境,來看看Vue和React的差異。

vue:在子元件中透過emit通知父元件執行特定事件

在子元件中定義emit,並且使用emit(事件名稱, 參數)的方式通知父元件更新。

// 子元件
<template>
  <li class="todo-item">
    <p :class="{'completed': todoItem.complete}">{{ todoItem.content }}</p>
    <input type="checkbox" :checked="todoItem.complete" @change="toggleComplete(todoItem.id)">
  </li>
</template>

<script setup>
defineProps({
  todoItem: {
    type: Object,
    required: true,
  }
});
// define要使用的emit
const emit = defineEmits(['toggle']);
const toggleComplete = (id) => {
  // emit的事件,以及事件中要帶的參數
  emit('toggle', id);
};
</script>

父元件則是需要綁定一個對應的事件名稱,來去接子元件提醒要進行的事件。

<template>
  <div class="container">
    <ul>
      <!-- 在父元件中透過v-bind去接會透過emit通知父元件進行的event -->
      <TodoItem v-for="todoItem in list" :key="todoItem.id" :todoItem="todoItem" @toggle="handleToggle"/>
    </ul>   
  </div>
</template>

<script setup>
import { ref } from 'vue';
import TodoItem from './components/TodoItem.vue';
const list = ref([
  {
    id: 1,
    content: '打掃家裡',
    complete: false,
  },
  {
    id: 2,
    content: '英文學習',
    complete: false,
  },

]);

const handleToggle = (id) => {
  list.value = list.value.map((item) => {
    if (item.id === id) {
      return {
        ...item,
        complete: !item.complete,
      }
    } else {
      return item;
    }
   });
};
</script>

在Vue的進行方式中,需要多做定義emit的步驟,但整體的進行方式依然是以事件觸發為中心概念下去更動父元件的state。

react:透過props接收父元件的function更新父元件state

子元件的部分是使用props進來的函式下去更新父元件的state

const TodoItem = ({ todoItem, toggleComplete }) => {
  return (
    <li className="todo-item">
      <p className={todoItem.complete ? 'completed' : ''}>{todoItem.content}</p>
      <input
        type="checkbox"
        checked={todoItem.complete}
        // 使用透過props傳來的函式,當事件觸發會是由父層的函式去改動子層的函式,函式還可以帶上參數
        onChange={() => toggleComplete(todoItem.id)}
      />
    </li>
  );
};

export default TodoItem;

父元件則須先宣告更新state的函式,並透過props帶到子元件中。

import { useState } from 'react';
import TodoItem from './TodoItem';

const App = () => {
  const [list, setList] = useState([
    {
      id: 1,
      content: '打掃家裡',
      complete: false,
    },
    {
      id: 2,
      content: '英文學習',
      complete: false,
    },
  ]);

  const handleToggle = (id) => {
    setList((previousList) =>
      previousList.map((item) => {
        if (item.id === id) {
          return {
            ...item,
            complete: !item.complete,
          };
        } else {
          return item;
        }
      })
    );
  };

  return (
    <div className="container">
      <ul>
        {list.map((todoItem) => (
          <TodoItem
            key={todoItem.id}
            todoItem={todoItem}
            // 透過props把改動元件內state的函式帶進子元件
            toggleComplete={handleToggle}
          />
        ))}
      </ul>
    </div>
  );
};

export default App;

React的進行方式雖然感覺像是從子元件中更新的父元件,但實際上子元件只是做了觸發事件的動作,因為實際上進行state改動動作的函式是父元件的函式,所以進行更新動作的人還是父元件。

總結

雖然Vue和React綁定事件的寫法不太一樣,從子層更新父層的寫法也不同,不過都還是透過「事件觸發」來通知父層進行自己state的更新。今天的內容個人覺得是初期從Vue轉換到React時,還算好懂的部分,因為只要掌握一個關鍵「透過事件觸發」,就不難理解。仔細對比一下的話,可能還會覺得React的寫法比較好記,因為就是在寫我們原本就懂的JavaScript,不需要額外再去記emit的用法。那今天的內容就先到這裡了,明天會來看與規劃共用元件有關的一個很有幫助的用法。


上一篇
【Day 12】傳遞state給子層用props,但props不只用來傳遞state
下一篇
【Day 14】設計樣式共用的元件!Vue有v-slot,那React可以怎麼做!?
系列文
從Vue學React!不只要會用,還要真的懂~30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言