最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

前提摘要:

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

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

核心在于標(biāo)記當(dāng)前項,來源項,目標(biāo)項,并且在拖拽完成時對數(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: "進(jìn)行任務(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: "進(jìn)行任務(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={"進(jìn)行中"} setIsDragSource={(isSrc) =>
          setDragSource(isSrc ? COLUMN_KEY_ONGONING : null)
        }
          setIsDragTarget={(isTarget) =>
            setDragTarget(isTarget ? COLUMN_KEY_ONGONING : null)
          }
          onDrop={handleDrop}>
          {/* <h2>進(jìn)行中</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)混亂??!如下所示:

首先問題定位,移動的來源項出現(xiàn)了問題,看代碼之后發(fā)現(xiàn)拖拽處理來源項沒有問題,那一定是那塊調(diào)用更新todaList出現(xiàn)了問題,問題定位在useEffect,使用時如果useEffect的第二個參數(shù)不傳就在組件所有更新都執(zhí)行(即任何時候),傳個空數(shù)組僅在掛載和卸載的時候執(zhí)行,或者傳個你想要去進(jìn)行更新時候去執(zhí)行(默認(rèn)情況下,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ù)組效果展示:

學(xué)習(xí)一個新的框架總是會進(jìn)行對比,

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

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

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

二:JSX 是什么?

學(xué)習(xí)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)進(jìn)門的,我是看完了官網(wǎng)的基礎(chǔ)才來學(xué)習(xí)課程的才覺得學(xué)習(xí)沒有那么吃力反而能加深理解;(老師的反其道而行可能不太適合初學(xué)者)

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

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

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

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

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

相關(guān)文章

  • ahooks封裝cookie?localStorage?sessionStorage方法

    ahooks封裝cookie?localStorage?sessionStorage方法

    這篇文章主要為大家介紹了ahooks封裝cookie?localStorage?sessionStorage的方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • 圖文示例講解useState與useReducer性能區(qū)別

    圖文示例講解useState與useReducer性能區(qū)別

    這篇文章主要為大家介紹了useState與useReducer性能區(qū)別圖文示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • react 不用插件實現(xiàn)數(shù)字滾動的效果示例

    react 不用插件實現(xiàn)數(shù)字滾動的效果示例

    這篇文章主要介紹了react 不用插件實現(xiàn)數(shù)字滾動的效果示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • 在react中使用tailwind的問題

    在react中使用tailwind的問題

    這篇文章主要介紹了在react中使用tailwind的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • 詳解React中組件之間通信的方式

    詳解React中組件之間通信的方式

    這篇文章主要介紹了React中組件之間通信的方式,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-07-07
  • React18之update流程從零實現(xiàn)詳解

    React18之update流程從零實現(xiàn)詳解

    這篇文章主要為大家介紹了React18之update流程從零實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • 一起來學(xué)習(xí)React元素的創(chuàng)建和渲染

    一起來學(xué)習(xí)React元素的創(chuàng)建和渲染

    這篇文章主要為大家詳細(xì)介紹了React元素的創(chuàng)建和渲染,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • react子組件接收的props賦值給state的陷阱問題

    react子組件接收的props賦值給state的陷阱問題

    這篇文章主要介紹了react子組件接收的props賦值給state的陷阱問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • React組件、狀態(tài)管理、代碼優(yōu)化的技巧

    React組件、狀態(tài)管理、代碼優(yōu)化的技巧

    文章總結(jié)了React組件設(shè)計、狀態(tài)管理、代碼組織和優(yōu)化的技巧,它涵蓋了使用Fragment、props解構(gòu)、defaultProps、key和ref的使用、渲染性能優(yōu)化等方面
    2024-11-11
  • reactrouter dom庫作用小結(jié)

    reactrouter dom庫作用小結(jié)

    `react-router-dom`是React應(yīng)用中用于實現(xiàn)頁面導(dǎo)航、嵌套路由、布局管理以及代碼分割和懶加載的重要庫,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-11-11

最新評論

会理县| 盐源县| 延津县| 法库县| 合水县| 邢台市| 迁西县| 永胜县| 比如县| 时尚| 赤城县| 张北县| 西宁市| 阿勒泰市| 内丘县| 垫江县| 政和县| 乌恰县| 威远县| 桑植县| 琼结县| 临沂市| 都匀市| 石河子市| 香港| 正宁县| 镇赉县| 嵊州市| 五指山市| 长阳| 和龙市| 光山县| 河源市| 崇州市| 安岳县| 长白| 罗甸县| 蓬安县| 宁陵县| 临沂市| 龙井市|