ฉันจะคงสถานะต้นไม้ Redx ไว้ในการรีเฟรชได้อย่างไร


92

เอกสารสำคัญอันดับแรกของเอกสาร Redux คือ:

สถานะของแอปพลิเคชันทั้งหมดของคุณจะถูกเก็บไว้ในโครงสร้างวัตถุภายในที่เก็บเดียว

และที่จริงฉันก็คิดว่าฉันเข้าใจหลักการทั้งหมดดี แต่ตอนนี้ฉันสับสนว่าแอปพลิเคชันหมายถึงอะไร

หากแอปพลิเคชันหมายถึงส่วนที่ซับซ้อนเพียงเล็กน้อยในเว็บไซต์และทำงานได้ในหน้าเดียวฉันเข้าใจ แต่ถ้าแอปพลิเคชันหมายถึงทั้งเว็บไซต์ล่ะ? ฉันควรใช้ LocalStorage หรือคุกกี้หรือบางสิ่งบางอย่างเพื่อเก็บสถานะต้นไม้หรือไม่ แต่ถ้าเบราว์เซอร์ไม่รองรับ LocalStorage ล่ะ?

ฉันอยากรู้ว่านักพัฒนารักษาต้นไม้ของตนไว้อย่างไร! :)


2
นั่นเป็นคำถามกว้าง ๆ คุณสามารถทำสิ่งต่างๆที่คุณกล่าวถึง คุณมีรหัสที่คุณต้องการแบ่งปันเพื่อแสดงให้เราเห็นว่าคุณลองทำอะไรแล้วไม่ได้ผลหรือไม่? คุณสามารถใช้เว็บไซต์ทั้งหมดของคุณให้เป็นเอนทิตีเดียวหรือจะมีหลาย ๆ คุณสามารถใช้ localStorage เพื่อคงข้อมูลหรือฐานข้อมูลจริงหรือไม่ก็ได้ แอปพลิเคชันหมายถึงการใช้ชีวิตอินสแตนซ์ที่ใช้งานอยู่ ในกรณีส่วนใหญ่นี่เป็นเพียงหนึ่งรูทของคุณ แต่อีกครั้งมีหลายวิธีในการใช้งานแอปพลิเคชัน
ZekeDroid

คำตอบ:


88

หากคุณต้องการคงสถานะ redux ของคุณในการรีเฟรชเบราว์เซอร์คุณควรทำเช่นนี้โดยใช้มิดเดิลแวร์ redux ตรวจสอบมิดเดิลแวร์ของหน่วยเก็บข้อมูลredux-persistและredux ทั้งคู่พยายามทำงานเดียวกันในการจัดเก็บสถานะ redux ของคุณเพื่อให้สามารถบันทึกและโหลดได้ตามต้องการ

-

แก้ไข

เป็นเวลานานแล้วที่ฉันได้อ่านคำถามนี้อีกครั้ง แต่เมื่อเห็นว่าอีกอัน (แม้ว่าจะมีคำตอบที่ได้รับการโหวตมากกว่า) สนับสนุนให้ใช้วิธีแก้ปัญหาของคุณเองฉันคิดว่าฉันจะตอบคำถามนี้อีกครั้ง

ในการแก้ไขนี้ห้องสมุดทั้งสองได้รับการอัปเดตภายในหกเดือนที่ผ่านมา ทีมของฉันใช้ redux-persist ในการผลิตมาสองสามปีแล้วและไม่มีปัญหาใด ๆ

แม้ว่าอาจดูเหมือนเป็นปัญหาง่ายๆ แต่คุณจะพบได้อย่างรวดเร็วว่าการใช้โซลูชันของคุณเองไม่เพียง แต่ทำให้เกิดภาระในการบำรุงรักษาเท่านั้น แต่ยังส่งผลให้เกิดข้อบกพร่องและปัญหาด้านประสิทธิภาพอีกด้วย ตัวอย่างแรกที่อยู่ในใจ ได้แก่ :

  1. JSON.stringifyและJSON.parseไม่เพียง แต่ส่งผลเสียต่อประสิทธิภาพเมื่อไม่จำเป็นเท่านั้น แต่ยังทำให้เกิดข้อผิดพลาดที่เมื่อไม่มีการจัดการในส่วนสำคัญของโค้ดเช่นร้านค้า redux ของคุณอาจทำให้แอปพลิเคชันของคุณเสียหาย
  2. (กล่าวไว้บางส่วนในคำตอบด้านล่าง): การหาเวลาและวิธีการบันทึกและกู้คืนสถานะแอปของคุณไม่ใช่ปัญหาง่ายๆ ทำบ่อยเกินไปและคุณจะทำร้ายประสิทธิภาพ ไม่เพียงพอหรือหากยังคงมีส่วนที่ไม่ถูกต้องคุณอาจพบว่าตัวเองมีข้อบกพร่องมากขึ้น ไลบรารีที่กล่าวถึงข้างต้นได้รับการทดสอบการต่อสู้ในแนวทางของพวกเขาและมีวิธีการปรับแต่งพฤติกรรมของพวกเขาเอง
  3. ส่วนหนึ่งของความสวยงามของ redux (โดยเฉพาะในระบบนิเวศของ React) คือความสามารถในการวางไว้ในสภาพแวดล้อมที่หลากหลาย ในการแก้ไขนี้ redux-persist มีการใช้งานพื้นที่เก็บข้อมูลที่แตกต่างกัน 15 แบบรวมถึงไลบรารี localForage ที่ยอดเยี่ยมสำหรับเว็บรวมถึงการสนับสนุน React Native, Electron และ Node

สรุปแล้วสำหรับ 3kB minified + gzipped (ในขณะที่ทำการแก้ไข) นี่ไม่ใช่ปัญหาฉันขอให้ทีมของฉันแก้ไขเอง


5
ฉันสามารถแนะนำ redux-persist ได้ (ยังไม่ได้ลองใช้ที่เก็บข้อมูล redux) แต่มันใช้งานได้ดีสำหรับฉันด้วยการกำหนดค่าและการตั้งค่าเพียงเล็กน้อย
larrydahooster

ณ วันที่นี้ห้องสมุดทั้งสองแห่งได้ถูกปิดตายและไม่ได้รับการบำรุงรักษาด้วยการกระทำครั้งสุดท้ายเมื่อ 2 ปีที่แล้ว
AnBisw

1
ดูเหมือนว่า redux-persist จะกลับมาเล็กน้อยโดยมีการเผยแพร่ใหม่เมื่อ 22 วันที่แล้วในขณะที่ฉันเขียน
Zeragamba

ตำแหน่งใหม่ของที่เก็บข้อมูลสำรองคือgithub.com/react-stack/redux-storage
Michael Freidgeim

2
หมายเหตุเกี่ยวกับคำตอบนี้:ความจริงก็คือซอฟต์แวร์และไลบรารีโดยทั่วไปได้นำแนวทางตามชุมชน (การสนับสนุน) มาใช้ซึ่งแม้แต่โมดูลที่สำคัญบางอย่างของภาษาการเขียนโปรแกรมก็ได้รับการสนับสนุนโดยบุคคลที่สาม โดยทั่วไปนักพัฒนาจะต้องจับตาดูเครื่องมือทุกอย่างที่ใช้ในสแต็กของเขาเพื่อให้ทราบว่ามันถูกเลิกใช้งาน / อัปเดตหรือไม่ สองทางเลือก; 1.ใช้งานของคุณเองและพัฒนาอย่างต่อเนื่องเพื่อให้มั่นใจถึงประสิทธิภาพและมาตรฐานข้ามแพลตฟอร์ม 2.ใช้โซลูชันที่ผ่านการทดสอบการต่อสู้และตรวจสอบเฉพาะการอัปเดต / คำแนะนำตามที่ @ MiFreidgeimSO-stopbeingevil กล่าว
Geek Guy

80

แก้ไข 25 ส.ค. 2562

ตามที่ระบุไว้ในหนึ่งในความคิดเห็น. เดิมRedux จัดเก็บแพคเกจได้ถูกย้ายไปทำปฏิกิริยากอง แนวทางนี้ยังคงมุ่งเน้นไปที่การนำโซลูชันการจัดการของรัฐของคุณไปใช้


คำตอบเดิม

ในขณะที่คำตอบที่ให้มานั้นใช้ได้ในบางจุดสิ่งสำคัญคือต้องสังเกตว่าแพคเกจพื้นที่เก็บข้อมูลสำรองดั้งเดิมได้เลิกใช้แล้วและไม่ได้รับการดูแลอีกต่อไป

ผู้เขียนเดิมของแพ็กเกจที่เก็บข้อมูลสำรองได้ตัดสินใจที่จะเลิกใช้งานโครงการและไม่ได้รับการดูแลอีกต่อไป

ตอนนี้หากคุณไม่ต้องการพึ่งพาแพ็กเกจอื่น ๆ เพื่อหลีกเลี่ยงปัญหาเช่นนี้ในอนาคตคุณสามารถม้วนโซลูชันของคุณเองได้ง่ายมาก

สิ่งที่คุณต้องทำคือ:

1- สร้างฟังก์ชันที่ส่งคืนสถานะจากlocalStorageนั้นส่งผ่านสถานะไปยังcreateStoreฟังก์ชัน redux ในพารามิเตอร์ที่สองเพื่อให้ความชุ่มชื้นแก่ร้านค้า

 const store = createStore(appReducers, state);

2- รับฟังการเปลี่ยนแปลงสถานะและทุกครั้งที่สถานะเปลี่ยนบันทึกสถานะเป็น localStorage

store.subscribe(() => {
    //this is just a function that saves state to localStorage
    saveState(store.getState());
}); 

และนั่นแหล่ะ ... ฉันใช้สิ่งที่คล้ายกันในการผลิตจริง ๆ แต่แทนที่จะใช้ฟังก์ชันฉันเขียนคลาสง่ายๆดังต่อไปนี้

class StateLoader {

    loadState() {
        try {
            let serializedState = localStorage.getItem("http://contoso.com:state");

            if (serializedState === null) {
                return this.initializeState();
            }

            return JSON.parse(serializedState);
        }
        catch (err) {
            return this.initializeState();
        }
    }

    saveState(state) {
        try {
            let serializedState = JSON.stringify(state);
            localStorage.setItem("http://contoso.com:state", serializedState);

        }
        catch (err) {
        }
    }

    initializeState() {
        return {
              //state object
            }
        };
    }
}

แล้วเมื่อบูตเครื่องแอปของคุณ ...

import StateLoader from "./state.loader"

const stateLoader = new StateLoader();

let store = createStore(appReducers, stateLoader.loadState());

store.subscribe(() => {
    stateLoader.saveState(store.getState());
});

หวังว่ามันจะช่วยใครสักคน

หมายเหตุประสิทธิภาพ

หากแอปพลิเคชันของคุณมีการเปลี่ยนแปลงสถานะบ่อยมากการบันทึกลงในพื้นที่จัดเก็บในตัวเครื่องบ่อยเกินไปอาจส่งผลกระทบต่อประสิทธิภาพของแอปพลิเคชันของคุณโดยเฉพาะอย่างยิ่งหากกราฟสถานะของวัตถุที่จะทำให้เป็นอนุกรม / deserialize มีขนาดใหญ่ สำหรับกรณีนี้คุณอาจต้องการที่จะ debounce หรือเค้นฟังก์ชั่นที่ช่วยประหยัดของรัฐเพื่อ localStorage ใช้RxJs, lodashหรือสิ่งที่คล้ายกัน


11
แทนที่จะใช้มิดเดิลแวร์ฉันชอบแนวทางนี้มากกว่า ขอบคุณสำหรับคำแนะนำเกี่ยวกับประสิทธิภาพ
โจโจว

1
คำตอบที่ต้องการแน่นอน อย่างไรก็ตามเมื่อฉันรีเฟรชเพจและโหลดสถานะจาก localstorage เมื่อสร้างที่เก็บฉันได้รับคำเตือนหลายอย่างซึ่งรวมถึงข้อความ "คุณสมบัติที่ไม่คาดคิด [ชื่อคอนเทนเนอร์] ที่พบในสถานะก่อนหน้าที่ตัวลดได้รับคาดว่าจะพบหนึ่งใน ชื่อคุณสมบัติตัวลดที่รู้จักแทน: "global", "language" คุณสมบัติที่ไม่คาดคิดจะถูกละเว้นมันยังคงใช้งานได้และโดยพื้นฐานแล้วบ่นว่า ณ จุดที่สร้างสโตร์นั้นไม่รู้เกี่ยวกับคอนเทนเนอร์อื่น ๆ ทั้งหมดหรือไม่ หลีกเลี่ยงคำเตือนนี้หรือไม่
Zief

@ Zief พูดยาก. ข้อความ "ดูเหมือน" ค่อนข้างชัดเจนตัวลดกำลังคาดหวังคุณสมบัติที่ไม่ได้ระบุไว้ อาจมีบางอย่างเกี่ยวข้องกับการระบุค่าเริ่มต้นให้กับสถานะซีเรียลไลซ์?
ลีโอ

วิธีแก้ปัญหาที่ตรงไปตรงมามาก ขอบคุณ.
Ishara Madawa

1
@ โจโจวชอบที่จะได้ยินว่าทำไมคุณถึงชอบแนวทางนี้ โดยส่วนตัวแล้วสิ่งนี้ดูเหมือนว่าเป็นตัวกลางที่แน่นอน
michaelgmcd

1

นี่เป็นไปตามคำตอบของลีโอ (ซึ่งควรเป็นคำตอบที่ยอมรับได้เนื่องจากบรรลุจุดประสงค์ของคำถามโดยไม่ต้องใช้ libs ของบุคคลที่สาม)

ฉันได้สร้างระดับซิงเกิลที่สร้างร้าน Redux, ยังคงมีอยู่โดยใช้การจัดเก็บในท้องถิ่นและช่วยให้การเข้าถึงที่ง่ายต่อการจัดเก็บผ่าน getter

ในการใช้งานเพียงใส่องค์ประกอบ Redux-Provider ต่อไปนี้รอบคลาสหลักของคุณ:

// ... Your other imports
import PersistedStore from "./PersistedStore";

ReactDOM.render(
  <Provider store={PersistedStore.getDefaultStore().store}>
    <MainClass />
  </Provider>,
  document.getElementById('root')
);

และเพิ่มชั้นเรียนต่อไปนี้ในโครงการของคุณ:

import {
  createStore
} from "redux";

import rootReducer from './RootReducer'

const LOCAL_STORAGE_NAME = "localData";

class PersistedStore {

  // Singleton property
  static DefaultStore = null;

  // Accessor to the default instance of this class
  static getDefaultStore() {
    if (PersistedStore.DefaultStore === null) {
      PersistedStore.DefaultStore = new PersistedStore();
    }

    return PersistedStore.DefaultStore;
  }

  // Redux store
  _store = null;

  // When class instance is used, initialize the store
  constructor() {
    this.initStore()
  }

  // Initialization of Redux Store
  initStore() {
    this._store = createStore(rootReducer, PersistedStore.loadState());
    this._store.subscribe(() => {
      PersistedStore.saveState(this._store.getState());
    });
  }

  // Getter to access the Redux store
  get store() {
    return this._store;
  }

  // Loading persisted state from localStorage, no need to access
  // this method from the outside
  static loadState() {
    try {
      let serializedState = localStorage.getItem(LOCAL_STORAGE_NAME);

      if (serializedState === null) {
        return PersistedStore.initialState();
      }

      return JSON.parse(serializedState);
    } catch (err) {
      return PersistedStore.initialState();
    }
  }

  // Saving persisted state to localStorage every time something
  // changes in the Redux Store (This happens because of the subscribe() 
  // in the initStore-method). No need to access this method from the outside
  static saveState(state) {
    try {
      let serializedState = JSON.stringify(state);
      localStorage.setItem(LOCAL_STORAGE_NAME, serializedState);
    } catch (err) {}
  }

  // Return whatever you want your initial state to be
  static initialState() {
    return {};
  }
}

export default PersistedStore;

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.