iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 17
0
自我挑戰組

菜雞們,讓我們一起征服JS及React吧系列 第 17

React菜雞-Day17:實戰!寫個To Do List - part2 - 讓app有血有肉有靈魂

  • 分享至 

  • xImage
  •  
tags: 鐵人賽 React javascript nodejs


鐵人賽第17天,週末假日,難得可以賴個床到八點半,但心裡掛念的還是今天的文章,不囉唆,立刻上工。/images/emoticon/emoticon69.gif

賦予ToDoApp靈魂

  • 昨天完成了初步的架構,今天我們要將input事件及reducer的動作,做個完整的結合。

啟動Input的onChange事件

  • 為了即時更新輸入的文字,我們在input.js中,新增一組state,同時觸發input 的 onChange事件,讓使用者輸入文字時,能即時更新到state之中,這樣,當我們按下send按鈕後,便可以直接從這個state取出輸入的文字,並進行ADD動作
  • msg: 暫存input欄位文字
  • setMsg: 存放input文字的stateFunc
// input.js
import React, { useContext, useState } from "react";
import { ContextStore } from "./ToDoApp";

export default function Input() {
  const [msg, setMsg] = useState("");
  const { appReducer } = useContext(ContextStore);
  const dispatch = appReducer[1];

  console.log("msg", msg); //<--放一個console驗證看看
  return (
    <div>
      <input
        style={{ width: "150px" }}
        onChange={(e) => setMsg(e.target.value)}
      ></input>
      <button>{"Send"}</button>
    </div>
  );
}
  • 開啟Chrome inspector中的console來驗證看看,Wow...果然每輸入一個文字,都有顯示喔

啟動Input的onClick事件

  • 為了讓輸入的文字順利觸發Reducer,我們必須建立一個input 的 onClick事件,並在過程中完成:
  • 包裝訊息為object格式,如:{newEvent:msg}
// input.js
import React, { useContext, useState } from "react";
import { ContextStore } from "./ToDoApp";

// Step1: 建立一個handleAddClick事件,負責包裝訊息以符合reducer格式,並回傳dispatch完成新增訊息的動作,
function handleAddClick(newMsg, dispatch) {
  const anEvent = { type: "ADD", newEvent: newMsg};
  return (e)=>dispatch(anEvent);     //<--為了讓handleAddClick func讓onClick使用,必須回傳一個func
}


// Step2: 我們在button新增一個onClick事件,並連結到建立一個handleAddClick事件
export default function Input() {
  const [msg, setMsg] = useState("");
  const { appReducer } = useContext(ContextStore);
  const dispatch = appReducer[1];

  console.log("msg", msg); //<--放一個console驗證看看
  return (
    <div>
      <input
        style={{ width: "150px" }}
        onChange={(e) => setMsg(e.target.value)}
      ></input>
      <button onClick={handleAddClick(msg, dispatch)}>{"Send"}</button> 
             
    </div>
  );
}

調整reducer

  • 我們先來檢視一下昨天的reducer,取得toDoList之後,我們會先運用switch判讀action.type確定它的動作,再到相對case進行處理。
  • 這裡我們直接回傳return toDoList.push(action.newEvent),來看看會發生什麼事...
// reducers/js
export default function reducers(toDoList, action) {
  switch (action.type) {
    case "ADD":
      return toDoList.push(action.newEvent);

    default:
      return toDoList;
  }
}

啥~toDoList不是一個Array???/images/emoticon/emoticon36.gif

  • 一開始你可能摸不著頭緒,不過,如果你可以用node來驗證一下。我們建立一個空的Array並利用push來新增一個文字Hello,你看看會回覆什麼訊息
let result = [].push("hello")
console.log(result) // 1
  • Array.push會回傳當下Array的長度,故我們不能直接回傳。而且,這樣的寫法也犯了幾個錯:
  1. React禁止我們直接修改state,故直接對reducer的state做修改,是行不通的。不過,我們可以用Array解構賦值的方式,複製一份toDoList並做修改。
  2. 不要直接回傳Array.push,應該分先push再回傳。
  • 修改如下:
// reducers.js
export default function reducers(toDoList, action) {
  let newToDoList = [...toDoList];
  switch (action.type) {
    case "ADD":
      newToDoList.push(action.newEvent); //<-- 先push
      break;
    default:
      console.log("toDoList", toDoList);  //<--直接印出舊的
      break;
  }

  return newToDoList;  //<--不管動作如何,直接回傳複製過來的newToDoList
}

}

成功了...疑有個Warning

import React, { useContext } from "react";
import { ContextStore } from "./ToDoApp";

export default function ToDoList() {
  const { appReducer } = useContext(ContextStore);
  const toDoList = appReducer[0];
  const result = toDoList.map((e, idx) => (<p key={`cmd-${idx}`}>{e}</p>));

  return result;
}

搞定了~ToDoApp距離完工不遠囉!/images/emoticon/emoticon30.gif

結論

  • 今天我們將ToDoApp中的事件處理reducer做完整的連結,讓事件可以順利的新增到toDoList之中。
  • 當然,程式還有很多部分待改善,我們明天來好好優化程式,賦予它更棒的使用者體驗(UX),加油~繼續Rock~~。
  • /images/emoticon/emoticon62.gif

上一篇
React菜雞-Day16:實戰!寫個To Do List - part1 - 先把架構弄出來
下一篇
React菜雞-Day18:實戰!寫個To Do List - part3 優化使用者體驗
系列文
菜雞們,讓我們一起征服JS及React吧30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言