欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React實現(xiàn)卡片拖拽效果流程詳解

 更新時間:2022年11月17日 09:05:07   作者:我不瘦但很逗  
這篇文章主要介紹了React Web開發(fā)實戰(zhàn)示例,實現(xiàn)卡片拖拽效果,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧

前提摘要:

學習宋一瑋 React 新版本 + 函數(shù)組件 &Hooks 優(yōu)先 開篇就是函數(shù)組件+Hooks 實現(xiàn)的效果如下: 學到第11篇了 照葫蘆畫瓢,不過老師在講解的過程中沒有考慮拖拽目標項邊界問題,我稍微處理了下這樣就實現(xiàn)拖拽流暢了

下面就是主要的代碼了,實現(xiàn)拖拽(src/App.js):

核心在于標記當前項,來源項,目標項,并且在拖拽完成時對數(shù)據(jù)處理,更新每一組數(shù)據(jù)(useState);

/** @jsxImportSource @emotion/react */
// 上面代碼是使用emotion的關(guān)鍵CSS-in-JS
import React, { useEffect, useState, useRef } from "react";
import { css } from "@emotion/react";
import "./App.css";
const MINUTE = 60 * 1000;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
const UPDATE_INTERVAL = MINUTE;
// const ongoingList = [{ title: "進行任務(wù)", status: "2022-11-09 15:29" }];
// const doneList = [{ title: "完成任務(wù)", status: "2022-11-09 15:59" }];
const KanbanBoard = ({ children }) => (
  <main
    css={css`
      flex: 10;
      display: flex;
      flex-direction: row;
      gap: 1rem;
      margin: 0 1rem 1rem;
    `}
  >
    {children}
  </main>
);
const KanbanColumn = ({
  children,
  className,
  title,
  setIsDragSource = () => { },
  setIsDragTarget = () => { },
  onDrop,
}) => {
  const combinedClassName = `kanban-column ${className}`;
  return (
    <section
      className={combinedClassName}
      onDragStart={() => setIsDragSource(true)}
      onDragOver={(evt) => {
        evt.preventDefault();
        evt.dataTransfer.dropEffect = "move";
        setIsDragTarget(true);
      }}
      onDragLeave={(evt) => {
        evt.preventDefault();
        evt.dataTransfer.dropEffect = "none";
        setIsDragTarget(false);
      }}
      onDrop={(evt) => {
        evt.preventDefault();
        onDrop && onDrop(evt);
      }}
      onDragEnd={(evt) => {
        evt.preventDefault();
        setIsDragSource(false);
        setIsDragTarget(false);
      }}
    >
      <h2>{title}</h2>
      <ul>{children}</ul>
    </section>
  );
};
const KanbanCard = ({ title, status, onDragStart }) => {
  const [displayTime, setDisplayTime] = useState(status);
  useEffect(() => {
    const updateDisplayTime = () => {
      const timePassed = new Date() - new Date(status);
      let relativeTime = "剛剛";
      if (MINUTE <= timePassed && timePassed < HOUR) {
        relativeTime = `${Math.ceil(timePassed / MINUTE)} 分鐘前`;
      } else if (HOUR <= timePassed && timePassed < DAY) {
        relativeTime = `${Math.ceil(timePassed / HOUR)} 小時前`;
      } else if (DAY <= timePassed) {
        relativeTime = `${Math.ceil(timePassed / DAY)} 天前`;
      }
      setDisplayTime(relativeTime);
    };
    const intervalId = setInterval(updateDisplayTime, UPDATE_INTERVAL);
    updateDisplayTime();
    return function cleanup() {
      clearInterval(intervalId);
    };
  }, [status]);
  const handleDragStart = (evt) => {
    evt.dataTransfer.effectAllowed = "move";
    evt.dataTransfer.setData("text/plain", title);
    onDragStart && onDragStart(evt);
  };
  return (
    <li className="kanban-card" draggable onDragStart={handleDragStart}>
      <div className="card-title">{title}</div>
      <div className="card-status">{displayTime}</div>
    </li>
  );
};
const AddKanbanCard = ({ onSubmit }) => {
  const [title, setTitle] = useState("");
  const handleChange = (evt) => {
    setTitle(evt.target.value);
  };
  const handleKeyDown = (evt) => {
    if (evt.key === "Enter") onSubmit(title);
  };
  const inputElem = useRef(null);
  useEffect(() => {
    inputElem.current.focus();
  });
  return (
    <li className="kanban-card">
      <h4>添加新卡片</h4>
      <div className="card-title">
        <input
          ref={inputElem}
          type="text"
          value={title}
          onChange={handleChange}
          onKeyDown={handleKeyDown}
        ></input>
      </div>
    </li>
  );
};
const DATE_STORE_KEY = "kanban_data_store";
const COLUMN_KEY_TODO = "todo";
const COLUMN_KEY_ONGONING = "ongoing";
const COLUMN_KEY_DONE = "done";
function App() {
  const [todoList, setTodoList] = useState([
    { title: "開發(fā)任務(wù)-1", status: "2022-05-22 18:15" },
  ]);
  const [ongoingList, setOngoingList] = useState([
    { title: "進行任務(wù)-1", status: "2022-08-22 18:15" },
  ]);
  const [doneList, setDoneList] = useState([
    { title: "完成任務(wù)-1", status: "2022-10-22 18:15" },
  ]);
  const [showAdd, setShowAdd] = useState(false);
  const handleAdd = (evt) => {
    setShowAdd(true);
  };
  const handleSubmit = (title) => {
    // todoList.unshift({title,status:new Date().toDateString()});
    setTodoList((current) => [{ title, status: new Date() + " " }, ...current]);
    setShowAdd(false);
  };
  const handleSaveAll = () => {
    const data = JSON.stringify({
      todoList,
      ongoingList,
      doneList,
    });
    window.localStorage.setItem(DATE_STORE_KEY, data);
  };
  useEffect(() => {
    const data = window.localStorage.getItem(DATE_STORE_KEY);
    setTimeout(() => {
      if (data) {
        const kanbanColumnData = JSON.parse(data);
        setTodoList(kanbanColumnData.todoList);
      }
    }, 1000);
  });
  const [draggedItem, setDraggedItem] = useState(null);
  const [dragSource, setDragSource] = useState(null);
  const [dragTarget, setDragTarget] = useState(null);
  const handleDrop = (evt) => {
    if (!draggedItem || !dragSource || !dragTarget || dragSource === dragTarget) { return; }
    const updaters = {
      [COLUMN_KEY_TODO]: setTodoList,
      [COLUMN_KEY_ONGONING]: setOngoingList,
      [COLUMN_KEY_DONE]: setDoneList
    };
    if (dragSource) {
      updaters[dragSource]((currentStat) => {
        return currentStat.filter((item) => !Object.is(item, draggedItem));
      });
    }
    if (dragTarget) {
      updaters[dragTarget]((currentStat) => {
        if (currentStat.length > 0) {
          return [draggedItem, ...currentStat]
        } else {
          return [draggedItem]
        }
      })
    }
  };
  return (
    <div className="App">
      <header className="App-header">
        <h1>
          我的看板<button onClick={handleSaveAll}>保存所有卡片</button>{" "}
        </h1>
      </header>
      <KanbanBoard>
        <KanbanColumn
          className="column-todo"
          title={
            <>
              待處理
              <button disabled={showAdd} onClick={handleAdd}>
                &#8853;添加新卡片
              </button>{" "}
            </>
          }
          setIsDragSource={(isSrc) =>
            setDragSource(isSrc ? COLUMN_KEY_TODO : null)
          }
          setIsDragTarget={(isTarget) =>
            setDragTarget(isTarget ? COLUMN_KEY_TODO : null)
          }
          onDrop={handleDrop}
        >
          {/* <h2>
            待處理
            <button disabled={showAdd} onClick={handleAdd}>
              &#8853;添加新卡片
            </button>{" "}
          </h2> */}
          {/* <ul> */}
          {showAdd && <AddKanbanCard onSubmit={handleSubmit} />}
          {todoList && todoList.map((item) => (
            <KanbanCard
              {...item}
              key={item.title}
              onDragStart={() => setDraggedItem(item)}
            />
          ))}
          {/* </ul> */}
        </KanbanColumn>
        <KanbanColumn className="column-ongoing" title={"進行中"} setIsDragSource={(isSrc) =>
          setDragSource(isSrc ? COLUMN_KEY_ONGONING : null)
        }
          setIsDragTarget={(isTarget) =>
            setDragTarget(isTarget ? COLUMN_KEY_ONGONING : null)
          }
          onDrop={handleDrop}>
          {/* <h2>進行中</h2>
          <ul> */}
          {ongoingList && ongoingList.map((item) => (
            <KanbanCard
              {...item}
              key={item.title}
              onDragStart={() => setDraggedItem(item)}
            />
          ))}
          {/* </ul> */}
        </KanbanColumn>
        <KanbanColumn className="column-done" title={"已處理"} setIsDragSource={(isSrc) =>
          setDragSource(isSrc ? COLUMN_KEY_DONE : null)
        }
          setIsDragTarget={(isTarget) =>
            setDragTarget(isTarget ? COLUMN_KEY_DONE : null)
          }
          onDrop={handleDrop}>
          {/* <h2>已處理</h2>
          <ul> */}
          {doneList && doneList.map((item) => (
            <KanbanCard
              {...item}
              key={item.title}
              onDragStart={() => setDraggedItem(item)}
            />
          ))}
          {/* </ul> */}
        </KanbanColumn>
      </KanbanBoard>
    </div>
  );
}
export default App;

這時拖拽基本完成,此時有一個bug,就是待處理添加新卡片的時候,拖拽之后的數(shù)據(jù)出現(xiàn)混亂?。∪缦滤荆?/p>

首先問題定位,移動的來源項出現(xiàn)了問題,看代碼之后發(fā)現(xiàn)拖拽處理來源項沒有問題,那一定是那塊調(diào)用更新todaList出現(xiàn)了問題,問題定位在useEffect,使用時如果useEffect的第二個參數(shù)不傳就在組件所有更新都執(zhí)行(即任何時候),傳個空數(shù)組僅在掛載和卸載的時候執(zhí)行,或者傳個你想要去進行更新時候去執(zhí)行(默認情況下,effect 將在每輪渲染結(jié)束后執(zhí)行,但你可以選擇讓它 在只有某些值改變的時候 才執(zhí)行。)

更改代碼如下:

  useEffect(() => {
    const data = window.localStorage.getItem(DATE_STORE_KEY);
    setTimeout(() => {
      if (data) {
        const kanbanColumnData = JSON.parse(data);
        setTodoList(kanbanColumnData.todoList);
      }
    }, 1000);
  },[]);

useEffect新增空數(shù)組效果展示:

學習一個新的框架總是會進行對比,

一:React的單項數(shù)據(jù)流和Vue中的雙向綁定有什么區(qū)別?

在我看了Vue 雙向綁定其實是語法糖罷了,其原理其實是Object.defineProperty()對數(shù)據(jù)進行劫持,監(jiān)聽到變化就去對數(shù)據(jù)進行更改;

而React 中的單項數(shù)據(jù)流做到了對原有數(shù)據(jù)的保護,你不能去直接去對Props進行更改,而是在需要賦值給State,然后再SetState中去進行更改,當然Hooks中提供了useState方法,使得開發(fā)者更加方便的去對數(shù)據(jù)進行處理和更改(我可太喜歡Hooks了很省事?。。?/p>

二:JSX 是什么?

學習React的時候,寫組件的時候?qū)戫撁嬖厥怯肑SX來寫的,即Render里面是用JSX來實現(xiàn)的,渲染之后其實質(zhì)是React.createElement,他只是語法糖 實現(xiàn)React組件的一部分而已,對比Vue中Template,(我更喜歡Vue的實現(xiàn),更符合開發(fā)者)不過Vue也可用JSX來實現(xiàn);

三:函數(shù)式組件(Hooks)與類組件(Class)優(yōu)缺點?

宋一瑋老師的數(shù)據(jù)表明函數(shù)式比類組件使用更多,并且函數(shù)組件基本上涵蓋了類組件的功能點,除了(只有類組件才能成為錯誤邊界)

從React官網(wǎng)中開局也是用類組件來領(lǐng)進門的,我是看完了官網(wǎng)的基礎(chǔ)才來學習課程的才覺得學習沒有那么吃力反而能加深理解;(老師的反其道而行可能不太適合初學者)

四:CSS可否想JS一樣應(yīng)用在組件中?

可以的,使用emotion來應(yīng)用到JSX中(主要代碼中有使用),不過CSS中傳入JS數(shù)據(jù)確實很方便但是在運行emotion時會創(chuàng)建大量的<style>標簽,有可能影響頁面性能。

CSS-in-JS 技術(shù)能幫我們做到樣式隔離、提升組件樣式的可維護性、可復(fù)用性。

五:常用的hooks——useState,useEffect、useRef

到此這篇關(guān)于React實現(xiàn)卡片拖拽效果流程詳解的文章就介紹到這了,更多相關(guān)React卡片拖拽內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React?component.forceUpdate()強制重新渲染方式

    React?component.forceUpdate()強制重新渲染方式

    這篇文章主要介紹了React?component.forceUpdate()強制重新渲染方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • 詳解react-router-dom v6版本基本使用介紹

    詳解react-router-dom v6版本基本使用介紹

    本文主要介紹了react-router-dom v6版本基本使用介紹,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • React Native自定義組件與輸出方法詳解

    React Native自定義組件與輸出方法詳解

    這篇文章主要給大家介紹了關(guān)于React Native自定義組件與輸出方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-07-07
  • ReactNative集成個推消息推送過程詳解

    ReactNative集成個推消息推送過程詳解

    這篇文章主要為大家介紹了ReactNative集成個推消息推送過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • react-native 封裝視頻播放器react-native-video的使用

    react-native 封裝視頻播放器react-native-video的使用

    本文主要介紹了react-native 封裝視頻播放器react-native-video的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-01-01
  • react-draggable實現(xiàn)拖拽功能實例詳解

    react-draggable實現(xiàn)拖拽功能實例詳解

    這篇文章主要給大家介紹了關(guān)于react-draggable實現(xiàn)拖拽功能的相關(guān)資料,React-Draggable一個使元素可拖動的簡單組件,文中通過代碼示例介紹的非常詳細,需要的朋友可以參考下
    2023-08-08
  • 淺談箭頭函數(shù)寫法在ReactJs中的使用

    淺談箭頭函數(shù)寫法在ReactJs中的使用

    這篇文章主要介紹了淺談箭頭函數(shù)寫法在ReactJs中的使用,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • 詳解React中的this指向

    詳解React中的this指向

    這篇文章主要介紹了React中的this指向的相關(guān)資料,幫助大家更好的理解和學習使用React,感興趣的朋友可以了解下
    2021-04-04
  • React 項目遷移 Webpack Babel7的實現(xiàn)

    React 項目遷移 Webpack Babel7的實現(xiàn)

    這篇文章主要介紹了React 項目遷移 Webpack Babel7的實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • React錯誤邊界Error Boundaries詳解

    React錯誤邊界Error Boundaries詳解

    錯誤邊界是一種React組件,這種組件可以捕獲發(fā)生在其子組件樹任何位置的JavaScript錯誤,并打印這些錯誤,同時展示降級UI,而并不會渲染那些發(fā)生崩潰的子組件樹
    2022-12-12

最新評論