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

詳解Jotai Immer如何實(shí)現(xiàn)undo redo功能示例詳解

 更新時(shí)間:2023年04月27日 15:31:16   作者:wesin  
這篇文章主要為大家介紹了詳解Jotai Immer如何實(shí)現(xiàn)undo redo功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景

之前項(xiàng)目中一直使用redux作為全局狀態(tài)庫(kù)使用,然后最近有個(gè)大功能改造,涉及undo、redo等功能影響,小伙伴推薦jotai來(lái)替代redux。于是稍事研究了下,對(duì)比redux,果然簡(jiǎn)單了很多,去除了redux里厚重的模板代碼,使用也很方便。 所以為啥一開始不直接就jotai呢,還抱著redux啃了半天?反思了下,可能原因是redux名聲更響,當(dāng)年擼flutter、擼ios的時(shí)候就被這個(gè)名詞轟炸了,所以一想到全局狀態(tài)庫(kù),就直接選了它,忽略了前端發(fā)展的日新月異,早就有了更好的替代品,經(jīng)驗(yàn)主義作祟,引以為戒。另一個(gè)原因估計(jì)是論壇里光顧著摸魚沸點(diǎn)了,忽略了介紹jotai的文章。 小伙伴又推薦了結(jié)合immer的功能來(lái)實(shí)現(xiàn)undo、redo功能。好嘛,翻了翻文檔,immer不就是個(gè)簡(jiǎn)化immutable對(duì)象修改的工具嗎?咋就跟undo、redo搭上關(guān)系了。經(jīng)解釋,方知原來(lái)immer高級(jí)篇有produceWithPatches的功能支持對(duì)象的undo以及redo功能。 當(dāng)然undo、redo功能我找了找其實(shí)是有現(xiàn)有庫(kù)的,只是代碼翻了翻多年沒(méi)更新了,下載過(guò)來(lái)連示例都運(yùn)行不起來(lái),算了,配合以上的jotai、immer功能,貌似直接簡(jiǎn)單擼一個(gè)也不難的樣子,那么就開干吧。

代碼不多,直接上了

import { Patch, applyPatches, produceWithPatches } from "immer";
import { atomWithImmer } from "jotai-immer";
import { atom, createStore } from "jotai/vanilla";
import { JSONSchema7 } from "json-schema";
interface HistoryInfo {
  patch: Patch;
  inverse: Patch;
}
interface HistoryState {
  undo: HistoryInfo[];
  redo: HistoryInfo[];
}
// 業(yè)務(wù)代碼涉及的undo、redo數(shù)據(jù)
export interface HistoryItem {
  businessA: { [key: string]: any };
  businessB: Record<string, JSONSchema7>;
  businessC: any;
}
// 構(gòu)建一個(gè)undo、redo的數(shù)據(jù)起點(diǎn)
const historyItemAtom = atomWithImmer<HistoryItem>({
  businessA: {},
  businessB: {},
  businessC: {},
});
// 觸發(fā)需要保存的undo的一個(gè)操作事件
export const fireHistoryAtom = atom(0.0);
export const businessAAtom = atomWithImmer<{ [key: string]: any }>({});
export const businessBAtom = atomWithImmer<Record<string, JSONSchema7>>({});
export const businessCAtom = atomWithImmer<any>();
export const historyAtom = atomWithImmer<HistoryState>({
  undo: [],
  redo: [],
});
export const store = createStore();
// 頁(yè)面數(shù)據(jù)加載完畢寫入初始化history
export const dataInit = () => {
  const newHis: HistoryItem = {
    businessA: store.get(businessAAtom),
    businessB: store.get(businessBAtom),
    businessC: store.get(businessCAtom),
  };
  store.set(historyItemAtom, newHis);
};
// ----------------------------------------------------------------
// atom subscriptions
store.sub(fireHistoryAtom, () => {
  const newHis: HistoryItem = {
    businessA: store.get(businessAAtom),
    businessB: store.get(businessBAtom),
    businessC: store.get(businessCAtom),
  };
  const oldHis = store.get(historyItemAtom);
  const [next, patch, inverse] = produceWithPatches(oldHis, (draft) => {
    draft = newHis;
    return draft;
  });
  store.set(historyItemAtom, next);
  store.set(historyAtom, (draft) => {
    draft.undo.push({
      patch: patch[0],
      inverse: inverse[0],
    });
    draft.redo = [];
  });
});
export const fireHistory = () => {
  setTimeout(() => {
    store.set(fireHistoryAtom, Math.random());
  }, 20);
};
// 執(zhí)行業(yè)務(wù)代碼
const doAction = (item: HistoryItem) => {
  store.set(businessAAtom, (draft) => {
    draft = item.businessA;
    return draft;
  });
  store.set(businessBAtom, (draft) => {
    draft = item.businessB;
    return draft;
  });
  store.set(businessCAtom, (draft) => {
    draft = item.businessC;
    return draft;
  });
  store.set(historyItemAtom, (draft) => {
    draft = item;
    return draft;
  });
};
export const undoAction = () => {
  const history = store.get(historyAtom);
  if (history.undo.length === 0) {
    return;
  }
  const old = history.undo[history.undo.length - 1];
  const currentItem = store.get(historyItemAtom);
  const item = applyPatches(currentItem, [old.inverse]);
  doAction(item);
  store.set(historyAtom, (draft) => {
    const current = draft.undo.pop();
    if (current) {
      draft.redo.push(current);
    }
  });
};
export const redoAction = () => {
  const history = store.get(historyAtom);
  if (history.redo.length === 0) {
    return;
  }
  const old = history.redo[history.redo.length - 1];
  const currentItem = store.get(historyItemAtom);
  const item = applyPatches(currentItem, [old.patch]);
  doAction(item);
  store.set(historyAtom, (draft) => {
    const current = draft.redo.pop();
    if (current) {
      draft.undo.push(current);
    }
  });
};

大致講下思路

定義 HistoryItem 作為undo、redo所需要恢復(fù)的業(yè)務(wù)數(shù)據(jù),可隨意擴(kuò)展。undo、redo本質(zhì)上就是你點(diǎn)了undo按鈕后你的數(shù)據(jù)需要恢復(fù)到上一個(gè)狀態(tài)。當(dāng)業(yè)務(wù)復(fù)雜了之后,你一次操作可能包含了多個(gè)數(shù)據(jù)的變化,而你undo的操作應(yīng)該是把這些數(shù)據(jù)一起還原。所以把涉及到變化的數(shù)據(jù)都包裝在一起,形成一個(gè)historyitem。通過(guò) immer提供的produceWithPatches生成撤銷和恢復(fù)數(shù)據(jù), 存在 HistoryState 的undo 里,然后你點(diǎn)擊undo按鈕,把數(shù)據(jù)從undo中取出,放入redo中。然后把數(shù)據(jù)狀態(tài)通過(guò)jotai全局修改,差不多就完成了。

簡(jiǎn)單的jotai使用是不需要store參與的,這里為了取數(shù)據(jù)、改數(shù)據(jù)方便,所以使用了store的監(jiān)聽和set功能。 使用store的代碼也很簡(jiǎn)單,直接在最外層包個(gè)provider即可

<Provider store={mainStore}>
	<MainPage />
</Provider>

使用方式應(yīng)該挺簡(jiǎn)單的吧。 當(dāng)你做完需要undo的操作后,調(diào)用fireHistory()函數(shù)即可。頁(yè)面的undo、redo按鈕可以直接把事件映射到undoAction、redoAction。至于undo、redo按鈕是否能點(diǎn)的功能可以直接讀取 historyAtom 里面undo、redo的數(shù)組長(zhǎng)度。

以上就是詳解Jotai Immer如何實(shí)現(xiàn)undo redo功能示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Jotai Immer實(shí)現(xiàn)undo redo的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React+CSS 實(shí)現(xiàn)繪制橫向柱狀圖

    React+CSS 實(shí)現(xiàn)繪制橫向柱狀圖

    這篇文章主要介紹了React+CSS 實(shí)現(xiàn)繪制橫向柱狀圖,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • 詳解React Fiber的工作原理

    詳解React Fiber的工作原理

    這篇文章主要介紹了React Fiber的工作原理的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用React框架,感興趣的朋友可以了解下
    2021-04-04
  • React?antd中setFieldsValu的簡(jiǎn)便使用示例代碼

    React?antd中setFieldsValu的簡(jiǎn)便使用示例代碼

    form.setFieldsValue是antd?Form組件中的一個(gè)方法,用于動(dòng)態(tài)設(shè)置表單字段的值,它接受一個(gè)對(duì)象作為參數(shù),對(duì)象的鍵是表單字段的名稱,值是要設(shè)置的字段值,這篇文章主要介紹了React?antd中setFieldsValu的簡(jiǎn)便使用,需要的朋友可以參考下
    2023-08-08
  • 使用 React Router Dom 實(shí)現(xiàn)路由導(dǎo)航的詳細(xì)過(guò)程

    使用 React Router Dom 實(shí)現(xiàn)路由導(dǎo)航的詳細(xì)過(guò)程

    React Router Dom 是 React 應(yīng)用程序中用于處理路由的常用庫(kù),它提供了一系列組件和 API 來(lái)管理應(yīng)用程序的路由,這篇文章主要介紹了使用 React Router Dom 實(shí)現(xiàn)路由導(dǎo)航,需要的朋友可以參考下
    2024-03-03
  • 你知道怎么在 HTML 頁(yè)面中使用 React嗎

    你知道怎么在 HTML 頁(yè)面中使用 React嗎

    這篇文章主要為大家詳細(xì)介紹了如何在HTML頁(yè)面中使用 React,使用使用js模塊化的方式開發(fā),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • React組件封裝中三大核心屬性詳細(xì)介紹

    React組件封裝中三大核心屬性詳細(xì)介紹

    這篇文章主要為大家介紹了React組件實(shí)例三大屬性state props refs使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • 詳解使用webpack+electron+reactJs開發(fā)windows桌面應(yīng)用

    詳解使用webpack+electron+reactJs開發(fā)windows桌面應(yīng)用

    這篇文章主要介紹了詳解使用webpack+electron+reactJs開發(fā)windows桌面應(yīng)用,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-02-02
  • React找不到模塊“./index.module.scss”或其相應(yīng)的類型聲明及解決方法

    React找不到模塊“./index.module.scss”或其相應(yīng)的類型聲明及解決方法

    這篇文章主要介紹了React找不到模塊“./index.module.scss”或其相應(yīng)的類型聲明及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • React實(shí)現(xiàn)基于Antd密碼強(qiáng)度校驗(yàn)組件示例詳解

    React實(shí)現(xiàn)基于Antd密碼強(qiáng)度校驗(yàn)組件示例詳解

    這篇文章主要為大家介紹了React實(shí)現(xiàn)基于Antd密碼強(qiáng)度校驗(yàn)組件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • React全局狀態(tài)管理的三種底層機(jī)制探究

    React全局狀態(tài)管理的三種底層機(jī)制探究

    近兩年前端技術(shù)的發(fā)展如火如荼,大量的前端項(xiàng)目都在使用或轉(zhuǎn)向 Vue 和 React 的陣營(yíng),由前端渲染頁(yè)面的單頁(yè)應(yīng)用占比也越來(lái)越高,這篇文章主要給大家介紹了關(guān)于React全局狀態(tài)管理的三種底層機(jī)制,需要的朋友可以參考下
    2021-09-09

最新評(píng)論

合肥市| 平陆县| 柞水县| 四川省| 礼泉县| 尤溪县| 朝阳市| 米泉市| 永康市| 无棣县| 张家界市| 原阳县| 拜城县| 湘潭市| 淮北市| 郸城县| 万载县| 友谊县| 宁化县| 新乡市| 巧家县| 德惠市| 于田县| 进贤县| 乐业县| 读书| 仲巴县| 高青县| 河池市| 宜兰县| 格尔木市| 加查县| 武清区| 贵定县| 沾化县| 尉氏县| 吉木萨尔县| 老河口市| 佛冈县| 孝感市| 日照市|