จะรีเซ็ตสถานะการจัดเก็บ Redux ได้อย่างไร?


455

ฉันใช้ Redux สำหรับการจัดการสถานะ
ฉันจะรีเซ็ตร้านค้าเป็นสถานะเริ่มต้นได้อย่างไร

ตัวอย่างเช่นสมมติว่าฉันมีบัญชีผู้ใช้สองบัญชี ( u1และu2)
ลองนึกภาพเหตุการณ์ต่อไปนี้:

  1. ผู้ใช้u1ลงชื่อเข้าใช้แอปและทำบางสิ่งเราจึงแคชข้อมูลบางอย่างในร้าน

  2. ผู้ใช้u1ออกจากระบบ

  3. ผู้ใช้u2ลงชื่อเข้าใช้แอปโดยไม่ต้องรีเฟรชเบราว์เซอร์

ณ จุดนี้ข้อมูลแคชจะถูกเชื่อมโยงกับu1และฉันต้องการที่จะทำความสะอาด

ฉันจะรีเซ็ตที่เก็บ Redux เป็นสถานะเริ่มต้นได้อย่างไรเมื่อผู้ใช้ออกจากระบบครั้งแรก


8
มันอาจจะดีกว่าคือการล้างของรัฐในการออกจากระบบแทน (จากมุมมองของการรักษาความปลอดภัย)
Clarkie

คำตอบ:


1034

วิธีหนึ่งในการทำเช่นนั้นคือการเขียนตัวลดทอนในแอปพลิเคชันของคุณ

combineReducers()ลดความรากปกติจะมอบหมายการจัดการการดำเนินการเพื่อลดที่สร้างขึ้นโดย อย่างไรก็ตามเมื่อใดก็ตามที่มันได้รับUSER_LOGOUTการกระทำก็จะส่งกลับสถานะเริ่มต้นอีกครั้ง

ตัวอย่างเช่นหากตัวลดทอนของคุณมีลักษณะดังนี้:

const rootReducer = combineReducers({
  /* your app’s top-level reducers */
})

คุณสามารถเปลี่ยนชื่อเป็นappReducerและเขียนการrootReducerมอบสิทธิ์ใหม่:

const appReducer = combineReducers({
  /* your app’s top-level reducers */
})

const rootReducer = (state, action) => {
  return appReducer(state, action)
}

ตอนนี้เราแค่ต้องสอนคนใหม่rootReducerให้กลับสู่สภาวะเริ่มต้นหลังจากUSER_LOGOUTการกระทำ ดังที่เราทราบแล้ว reducers ควรจะคืนค่าสถานะเริ่มต้นเมื่อพวกเขาถูกเรียกว่าundefinedเป็นอาร์กิวเมนต์แรกไม่ว่าการกระทำ ลองใช้ความจริงนี้เพื่อตัดการสะสมstateตามที่เราส่งไปที่appReducer:

 const rootReducer = (state, action) => {
  if (action.type === 'USER_LOGOUT') {
    state = undefined
  }

  return appReducer(state, action)
}

ตอนนี้เมื่อใดก็ตามที่USER_LOGOUTเกิดเพลิงไหม้ reducers ทั้งหมดจะเริ่มต้นใหม่อีกครั้ง พวกเขาสามารถคืนสิ่งที่แตกต่างจากตอนแรกถ้าพวกเขาต้องการเพราะพวกเขาสามารถตรวจสอบได้action.typeเช่นกัน

หากต้องการย้ำรหัสใหม่แบบเต็มจะมีลักษณะดังนี้:

const appReducer = combineReducers({
  /* your app’s top-level reducers */
})

const rootReducer = (state, action) => {
  if (action.type === 'USER_LOGOUT') {
    state = undefined
  }

  return appReducer(state, action)
}

โปรดทราบว่าฉันไม่ได้กลายพันธุ์รัฐที่นี่ฉันเป็นเพียง มอบหมายการอ้างอิงตัวแปรท้องถิ่นที่เรียกว่าstateก่อนที่จะส่งไปยังฟังก์ชั่นอื่น การกลายพันธุ์วัตถุของรัฐจะเป็นการละเมิดหลักการ Redux

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

ก่อนอื่นคุณต้องนำเข้าเครื่องมือจัดเก็บข้อมูลที่เหมาะสมจากนั้นจึงแยกวิเคราะห์สถานะก่อนที่จะตั้งค่าundefinedและทำความสะอาดคีย์สถานะการจัดเก็บแต่ละรายการ

const rootReducer = (state, action) => {
    if (action.type === SIGNOUT_REQUEST) {
        // for all keys defined in your persistConfig(s)
        storage.removeItem('persist:root')
        // storage.removeItem('persist:otherKey')

        state = undefined;
    }
    return appReducer(state, action);
};

15
ฉันอยากรู้อยากเห็น Dan คุณช่วยทำอะไรแบบนี้กับตัวลดของคุณได้ไหม ด้วย CLEAR_DATA จะเป็นการกระทำ case 'CLEAR_DATA': return initialState
HussienK

6
@HussienK ที่จะทำงาน แต่ไม่ได้อยู่ในสถานะสำหรับลดทุก
คอรีแดเนียล

12
นี่คือรุ่นที่คุณรวมตัวลดแบบไดนามิกในกรณีที่คุณใช้ตัวexport const createRootReducer = asyncReducers => { const appReducer = combineReducers({ myReducer ...asyncReducers }); return (state, action) => { if (action.type === 'LOGOUT_USER') { state = undefined; } return appReducer(state, action); } };
ลดแบบอะซิงโครนัส

2
if (action.type === 'RESET') return action.stateFromLocalStorage
Dan Abramov

3
วิธีการนี้ทำให้รัฐและประวัติศาสตร์ทั้งหมดมีความชัดเจนหรือไม่? ฉันกำลังคิดจากมุมมองด้านความปลอดภัย: ถ้าสิ่งนี้ได้รับการดำเนินการเมื่อการUSER_LOGOUTดำเนินการถูกไล่ออกไปแล้วเป็นไปได้หรือไม่ที่จะได้รับข้อมูลสถานะจากก่อนหน้านี้? (เช่นผ่าน devtools)
AlexKempton

81

ฉันต้องการชี้ให้เห็นว่าความคิดเห็นที่ยอมรับโดย Dan Abramov นั้นถูกต้องยกเว้นว่าเราพบปัญหาแปลก ๆ เมื่อใช้แพ็คเกจ react-router-redux พร้อมกับวิธีการนี้ การแก้ไขของเราคือการไม่ตั้งค่าสถานะundefinedแต่ยังคงใช้ตัวลดเส้นทางการกำหนดปัจจุบัน ดังนั้นฉันขอแนะนำให้ใช้วิธีการแก้ปัญหาด้านล่างหากคุณกำลังใช้แพ็คเกจนี้

const rootReducer = (state, action) => {
  if (action.type === 'USER_LOGOUT') {
    const { routing } = state
    state = { routing } 
  }
  return appReducer(state, action)
}

19
ฉันคิดว่าของที่นี่คือคุณอาจไม่ต้องการที่จะเคลียร์ทรีของรัฐทั้งหมดในการออกจากระบบ - วิธีการทำงานได้ดีพอ ๆ กันที่ตัวลดรากของทรีย่อยใด ๆ และดังนั้นจึงอาจชัดเจนกว่าที่จะใช้เทคนิคนี้เฉพาะที่ ทรีย่อย (s) ที่คุณไม่ต้องการล้างมากกว่าการเลือกออก 'พิเศษ' เด็กที่จะไม่ชัดเจนที่ลดรากของต้นไม้ทั้งหมดเช่นนี้
davnicwil

1
ฉันคิดว่าฉันกำลังประสบปัญหานี้คุณกำลังอ้างถึงในขณะนี้ (ที่ออกจากระบบมันจะกำหนดเส้นทางที่ถูกต้อง แต่ส่วนประกอบที่แตกต่างกันจะโหลด) ฉันใช้สิ่งที่คล้ายกับของคุณเพื่อแก้ไข แต่ฉันคิดว่าบางสิ่งบางอย่างด้วย js ที่ไม่เปลี่ยนรูปจะรวมตัวกันเป็นจำนวนมาก ฉันลงเอยด้วยการสร้างลดพาเรนต์ที่มีการดำเนินการ RESET-STATE และฉันได้รับสืบทอดจากตัวลดขนาดนั้นเพื่อหลีกเลี่ยงการแตะเส้นทางโดยรวม
Neta Meta

พบปัญหาที่คล้ายกันนี้ได้รับการแก้ไข ขอบคุณ
Lloyd Watkin

3
โปรดทราบว่าด้วย react-redux-router คุณสมบัติจะเป็นrouterและไม่ใช่rounting
Mrchief

2
@Mrchief มันขึ้นอยู่กับสิ่งที่คุณกำหนดไว้ว่ามันเป็นในของคุณcombineReducers()..... ถ้าคุณมีcombineReducers({routing: routingReducer})มันจะเป็นไปตามที่อธิบายไว้ในคำตอบ
เบน Lonsdale

40

กำหนดการกระทำ:

const RESET_ACTION = {
  type: "RESET"
}

จากนั้นในตัวลดขนาดแต่ละตัวของคุณโดยสมมติว่าคุณกำลังใช้switchหรือif-elseจัดการหลายการกระทำผ่านตัวลดแต่ละตัว switchฉันจะใช้กรณีสำหรับ

const INITIAL_STATE = {
  loggedIn: true
}

const randomReducer = (state=INITIAL_STATE, action) {
  switch(action.type) {
    case 'SOME_ACTION_TYPE':

       //do something with it

    case "RESET":

      return INITIAL_STATE; //Always return the initial state

   default: 
      return state; 
  }
}

วิธีนี้เมื่อใดก็ตามที่คุณเรียกRESETการกระทำคุณลดจะปรับปรุงร้านค้าที่มีสถานะเริ่มต้น

ตอนนี้สำหรับการออกจากระบบคุณสามารถจัดการได้ดังนี้:

const logoutHandler = () => {
    store.dispatch(RESET_ACTION)
    // Also the custom logic like for the rest of the logout handler
}

ทุกครั้งที่ผู้ใช้เข้าใช้โดยไม่ต้องรีเฟรชเบราว์เซอร์ ร้านค้าจะเป็นค่าเริ่มต้นเสมอ

store.dispatch(RESET_ACTION)เพียงอธิบายความคิด คุณมักจะมีผู้สร้างแอคชั่นเพื่อจุดประสงค์ LOGOUT_ACTIONวิธีที่ดีกว่ามากจะเป็นไปได้ว่าคุณมี

LOGOUT_ACTIONเมื่อคุณส่งนี้ มิดเดิลแวร์แบบกำหนดเองสามารถดักจับการกระทำนี้ได้ด้วย Redux-Saga หรือ Redux-Thunk อย่างไรก็ตามทั้งสองวิธีคุณสามารถส่งการกระทำอีก 'RESET' วิธีนี้จะทำให้การออกจากระบบและการตั้งค่าใหม่ของร้านค้าเกิดขึ้นพร้อมกันและร้านค้าของคุณจะพร้อมสำหรับการเข้าสู่ระบบของผู้ใช้คนอื่น


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

3
@worc รัฐจะไม่จริงไม่ได้กำหนดเพราะ reducers กลับ initialState เมื่อพวกเขาได้รับการรัฐไม่ได้กำหนด
กีโยม

3
@worc คิดว่าด้วยวิธีการนี้ทุกครั้งที่ทุกคนสร้างตัวลดใหม่คุณจะต้องจำไว้ว่าให้เพิ่มตัวพิมพ์ใหม่
Francute

1
ผมเคยเปลี่ยนแปลงแน่นอนใจของฉันเกี่ยวกับเรื่องนี้ด้วยเหตุผลทั้งผู้ที่บวกความคิดที่ว่า RESET_ACTION เป็นการกระทำ ดังนั้นจึงไม่ได้อยู่ในตัวลดที่เริ่มต้นด้วย
worc

1
นี่เป็นแนวทางที่ถูกต้องอย่างแน่นอน การตั้งค่าสถานะเป็นอะไรก็ได้ แต่รัฐเริ่มต้นกำลังขอปัญหา
เซบาสเตียนเซร์ราโน่

15

เพียงคำตอบที่ง่ายสำหรับคำตอบที่ดีที่สุด:

const rootReducer = combineReducers({
    auth: authReducer,
    ...formReducers,
    routing
});


export default (state, action) =>
  rootReducer(action.type === 'USER_LOGOUT' ? undefined : state, action);

มันใช้งานได้ดีขอบคุณฉันมาจากคำตอบของแดน แต่ฉันไม่สามารถเข้าใจได้
Aljohn Yamaro

14
 const reducer = (state = initialState, { type, payload }) => {

   switch (type) {
      case RESET_STORE: {
        state = initialState
      }
        break
   }

   return state
 }

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


10

ด้วย Redux ถ้าใช้วิธีแก้ไขปัญหาต่อไปนี้ซึ่งถือว่าฉันได้ตั้งค่า initialState ใน reducers ทั้งหมดของฉัน (เช่น {ผู้ใช้: {ชื่ออีเมล}}) ในหลาย ๆ องค์ประกอบฉันตรวจสอบคุณสมบัติซ้อนเหล่านี้ดังนั้นด้วยการแก้ไขนี้ฉันป้องกันวิธีการแสดงผลของฉันเสียในเงื่อนไขคุณสมบัติคู่ (เช่นถ้า state.user.email ซึ่งจะโยนผู้ใช้ข้อผิดพลาดจะไม่ได้กำหนดถ้าโซลูชั่นที่กล่าวถึงด้านบน)

const appReducer = combineReducers({
  tabs,
  user
})

const initialState = appReducer({}, {})

const rootReducer = (state, action) => {
  if (action.type === 'LOG_OUT') {
    state = initialState
  }

  return appReducer(state, action)
}

7

อัพเดท NGRX4

หากคุณย้ายไปยัง NGRX 4 คุณอาจสังเกตเห็นจากคู่มือการโยกย้ายว่าวิธีการลดความเสี่ยงสำหรับการรวมตัวลดของคุณถูกแทนที่ด้วยวิธีการ ActionReducerMap ในตอนแรกวิธีการใหม่ในการทำสิ่งต่าง ๆ อาจทำให้การรีเซ็ตเป็นความท้าทาย มันตรงไปตรงมาจริง ๆ แต่วิธีการทำเช่นนี้มีการเปลี่ยนแปลง

วิธีการแก้ปัญหานี้ได้รับแรงบันดาลใจจากส่วน API meta-reducers ของNGRX4 Github docs

ก่อนอื่นสมมติว่าคุณกำลังรวมตัวลดขนาดเช่นนี้โดยใช้ตัวเลือก ActionReducerMap ใหม่ของ NGRX:

//index.reducer.ts
export const reducers: ActionReducerMap<State> = {
    auth: fromAuth.reducer,
    layout: fromLayout.reducer,
    users: fromUsers.reducer,
    networks: fromNetworks.reducer,
    routingDisplay: fromRoutingDisplay.reducer,
    routing: fromRouting.reducer,
    routes: fromRoutes.reducer,
    routesFilter: fromRoutesFilter.reducer,
    params: fromParams.reducer
}

ตอนนี้สมมติว่าคุณต้องการรีเซ็ตสถานะจากภายใน app.module `

//app.module.ts
import { IndexReducer } from './index.reducer';
import { StoreModule, ActionReducer, MetaReducer } from '@ngrx/store';
...
export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
    return function(state, action) {

      switch (action.type) {
          case fromAuth.LOGOUT:
            console.log("logout action");
            state = undefined;
      }

      return reducer(state, action);
    }
  }

  export const metaReducers: MetaReducer<any>[] = [debug];

  @NgModule({
    imports: [
        ...
        StoreModule.forRoot(reducers, { metaReducers}),
        ...
    ]
})

export class AppModule { }

`

และนั่นเป็นวิธีหนึ่งในการบรรลุผลเดียวกันกับ NGRX 4


5

เมื่อรวมวิธีการของ Dan, Ryan และ Rob เข้าด้วยกันเพื่อรักษาrouterสถานะและกำหนดค่าเริ่มต้นทุกอย่างในแผนผังสถานะฉันจึงจบลงด้วยสิ่งนี้:

const rootReducer = (state, action) => appReducer(action.type === LOGOUT ? {
    ...appReducer({}, {}),
    router: state && state.router || {}
  } : state, action);

4

ฉันได้สร้างส่วนประกอบเพื่อให้ Redux ความสามารถในการรีเซ็ตสถานะคุณเพียงแค่ต้องใช้ส่วนประกอบนี้เพื่อปรับปรุงร้านค้าของคุณและส่งแอ็คชั่นเฉพาะประเภทเพื่อเรียกการรีเซ็ต ความคิดในการนำไปปฏิบัตินั้นเหมือนกับสิ่งที่ @Dan Abramov พูด

Github: https://github.com/wwayne/redux-reset


4

ฉันได้สร้างการกระทำเพื่อล้างสถานะ ดังนั้นเมื่อฉันส่งผู้สร้างแอ็คชั่นล็อกเอาต์ฉันก็ส่งการกระทำเพื่อล้างสถานะด้วย

การกระทำของผู้ใช้บันทึก

export const clearUserRecord = () => ({
  type: CLEAR_USER_RECORD
});

ผู้สร้างแอ็คชันออกจากระบบ

export const logoutUser = () => {
  return dispatch => {
    dispatch(requestLogout())
    dispatch(receiveLogout())
    localStorage.removeItem('auth_token')
    dispatch({ type: 'CLEAR_USER_RECORD' })
  }
};

ลด

const userRecords = (state = {isFetching: false,
  userRecord: [], message: ''}, action) => {
  switch (action.type) {
    case REQUEST_USER_RECORD:
    return { ...state,
      isFetching: true}
    case RECEIVE_USER_RECORD:
    return { ...state,
      isFetching: false,
      userRecord: action.user_record}
    case USER_RECORD_ERROR:
    return { ...state,
      isFetching: false,
      message: action.message}
    case CLEAR_USER_RECORD:
    return {...state,
      isFetching: false,
      message: '',
      userRecord: []}
    default:
      return state
  }
};

ฉันไม่แน่ใจว่าสิ่งนี้เหมาะสมหรือไม่


2

หากคุณกำลังใช้Redux กระทำนี่เป็นวิธีแก้ปัญหาอย่างรวดเร็วโดยใช้ฮอฟ ( การสั่งซื้อที่สูงขึ้นฟังก์ชั่น ) handleActionsสำหรับ

import { handleActions } from 'redux-actions';

export function handleActionsEx(reducer, initialState) {
  const enhancedReducer = {
    ...reducer,
    RESET: () => initialState
  };
  return handleActions(enhancedReducer, initialState);
}

จากนั้นใช้handleActionsExแทนต้นฉบับhandleActionsเพื่อจัดการตัวลด

คำตอบของแดนจะช่วยให้ความคิดที่ดีเกี่ยวกับปัญหานี้ redux-persistแต่มันไม่ได้ผลงานออกมาได้ดีสำหรับฉันเพราะฉันใช้
เมื่อใช้กับredux-persistเพียงผ่านundefinedสถานะไม่ได้ก่อให้เกิดพฤติกรรมถาวรดังนั้นฉันรู้ว่าฉันต้องลบรายการจากการจัดเก็บด้วยตนเอง (React พื้นเมืองในกรณีของฉันดังนั้นAsyncStorage)

await AsyncStorage.removeItem('persist:root');

หรือ

await persistor.flush(); // or await persistor.purge();

ไม่ได้ผลสำหรับฉันเช่นกัน - พวกเขาตะโกนใส่ฉัน (เช่นการร้องเรียนเช่น"คีย์ที่ไม่คาดคิด _persist ... " )

จากนั้นฉันก็ครุ่นคิดในสิ่งที่ฉันต้องการเพียงแค่ทำให้ทุกคนลดระดับตัวเองคืนสถานะเริ่มต้นของตัวเองเมื่อRESETพบกับประเภทการกระทำ ด้วยวิธีนี้การจัดการอย่างต่อเนื่องตามธรรมชาติ เห็นได้ชัดว่าไม่มีฟังก์ชั่นยูทิลิตี้ข้างต้น ( handleActionsEx) รหัสของฉันจะไม่ดูแห้ง (แม้ว่ามันจะเป็นเพียงซับเดียวเช่นRESET: () => initialState ) แต่ฉันไม่สามารถยืนได้เพราะฉันรัก metaprogramming


2

วิธีแก้ปัญหาต่อไปนี้ใช้ได้กับฉัน

ฉันได้เพิ่มฟังก์ชั่นการตั้งค่าสถานะใหม่เป็น meta reducers คีย์คือใช้

return reducer(undefined, action);

เพื่อตั้งค่าตัวลดทั้งหมดให้อยู่ในสถานะเริ่มต้น การคืนundefinedแทนนั้นก่อให้เกิดข้อผิดพลาดเนื่องจากความจริงที่ว่าโครงสร้างของร้านค้าถูกทำลาย

/reducers/index.ts

export function resetState(reducer: ActionReducer<State>): ActionReducer<State> {
  return function (state: State, action: Action): State {

    switch (action.type) {
      case AuthActionTypes.Logout: {
        return reducer(undefined, action);
      }
      default: {
        return reducer(state, action);
      }
    }
  };
}

export const metaReducers: MetaReducer<State>[] = [ resetState ];

app.module.ts

import { StoreModule } from '@ngrx/store';
import { metaReducers, reducers } from './reducers';

@NgModule({
  imports: [
    StoreModule.forRoot(reducers, { metaReducers })
  ]
})
export class AppModule {}

2

จากมุมมองของการรักษาความปลอดภัยเป็นสิ่งที่ปลอดภัยที่สุดที่จะทำเมื่อเข้าสู่ระบบออกมาใช้คือการตั้งค่าทั้งหมดรัฐถาวร (คุกกี้อดีตlocalStorage, IndexedDB, Web SQLฯลฯ ) window.location.reload()และทำในการฟื้นฟูอย่างหนักของหน้าโดยใช้ อาจเป็นไปได้ว่านักพัฒนาที่เลอะเทอะโดยไม่ตั้งใจหรือตั้งใจเก็บข้อมูลที่มีความละเอียดอ่อนบางอย่างwindowใน DOM และอื่น ๆ การพัดพาสถานะที่ค้างอยู่ทั้งหมดและการรีเฟรชเบราว์เซอร์เป็นวิธีเดียวที่จะรับประกันว่าจะไม่มีข้อมูลจากผู้ใช้ก่อนหน้านี้

(แน่นอนว่าในฐานะผู้ใช้บนคอมพิวเตอร์ที่ใช้ร่วมกันคุณควรใช้โหมด "การเบราส์ส่วนตัว" ปิดหน้าต่างเบราว์เซอร์ด้วยตัวเองใช้ฟังก์ชั่น "ล้างข้อมูลการท่องเว็บ" ฯลฯ แต่ในฐานะนักพัฒนาเราไม่สามารถคาดหวังได้ว่าทุกคน ที่ขยัน)


1
ทำไมผู้คนถึงลงคะแนนสิ่งนี้ เมื่อคุณทำสถานะ redux ใหม่ที่มีเนื้อหาว่างเปล่าโดยทั่วไปคุณยังคงมีสถานะก่อนหน้าในหน่วยความจำและคุณสามารถเข้าถึงข้อมูลจากพวกเขาในทางทฤษฎี การรีเฟรชเบราว์เซอร์คือทางออกที่ปลอดภัยที่สุดของคุณ!
Wilhelm Sorban

2

วิธีแก้ปัญหาของฉันเมื่อทำงานกับ typescript สร้างขึ้นจากคำตอบของ Dan (การพิมพ์แบบ Redux ทำให้ไม่สามารถผ่านundefinedไปยัง reducer เป็นอาร์กิวเมนต์แรกได้ดังนั้นฉันจะแคชสถานะ root เริ่มต้นในค่าคงที่)

// store

export const store: Store<IStoreState> = createStore(
  rootReducer,
  storeEnhacer,
)

export const initialRootState = {
  ...store.getState(),
}

// root reducer

const appReducer = combineReducers<IStoreState>(reducers)

export const rootReducer = (state: IStoreState, action: IAction<any>) => {
  if (action.type === "USER_LOGOUT") {
    return appReducer(initialRootState, action)
  }

  return appReducer(state, action)
}


// auth service

class Auth {
  ...

  logout() {
    store.dispatch({type: "USER_LOGOUT"})
  }
}

2

คำตอบที่ยอมรับได้ช่วยฉันแก้ปัญหากรณีของฉัน อย่างไรก็ตามฉันพบกรณีที่ไม่ต้องล้างสถานะทั้งหมด ดังนั้น - ฉันทำอย่างนี้:

const combinedReducer = combineReducers({
    // my reducers 
});

const rootReducer = (state, action) => {
    if (action.type === RESET_REDUX_STATE) {
        // clear everything but keep the stuff we want to be preserved ..
        delete state.something;
        delete state.anotherThing;
    }
    return combinedReducer(state, action);
}

export default rootReducer;

หวังว่านี่จะช่วยคนอื่น :)


ถ้าฉันมีมากกว่า 10 สถานะ แต่ฉันต้องการรีเซ็ตสถานะของตัวลดเพียงอันเดียว
พอล

1

เพียงแค่ส่วนขยายของ@ dan-abramovคำตอบบางครั้งเราอาจจำเป็นต้องเก็บคีย์บางอย่างจากการรีเซ็ต

const retainKeys = ['appConfig'];

const rootReducer = (state, action) => {
  if (action.type === 'LOGOUT_USER_SUCCESS' && state) {
    state = !isEmpty(retainKeys) ? pick(state, retainKeys) : undefined;
  }

  return appReducer(state, action);
};

1

ตัวเลือกที่ง่ายและรวดเร็วซึ่งใช้งานได้สำหรับฉันคือการใช้redux-reset Reduxซึ่งตรงไปตรงมาและมีตัวเลือกขั้นสูงสำหรับแอปขนาดใหญ่

ตั้งค่าในสร้างร้านค้า

import reduxReset from 'redux-reset'
...
const enHanceCreateStore = compose(
applyMiddleware(...),
reduxReset()  // Will use 'RESET' as default action.type to trigger reset
)(createStore)
const store = enHanceCreateStore(reducers)

ส่ง 'รีเซ็ต' ของคุณในฟังก์ชันออกจากระบบ

store.dispatch({
type: 'RESET'
})

หวังว่านี่จะช่วยได้


1

ฉันใช้เพื่อป้องกันไม่ให้ Redux อ้างอิงถึงตัวแปรเดียวกันของสถานะเริ่มต้น:

// write the default state as a function
const defaultOptionsState = () => ({
  option1: '',
  option2: 42,
});

const initialState = {
  options: defaultOptionsState() // invoke it in your initial state
};

export default (state = initialState, action) => {

  switch (action.type) {

    case RESET_OPTIONS:
    return {
      ...state,
      options: defaultOptionsState() // invoke the default function to reset this part of the state
    };

    default:
    return state;
  }
};

ความคิดในการเขียนสถานะเริ่มต้นเป็นฟังก์ชั่นที่บันทึกจริงวันที่นี่ ขอบคุณ
Chris

0

วิธีนี้เหมาะสมมาก: ทำลายสถานะ "NAME" ที่เฉพาะเจาะจงเพื่อเพิกเฉยและรักษาผู้อื่น

const rootReducer = (state, action) => {
    if (action.type === 'USER_LOGOUT') {
        state.NAME = undefined
    }
    return appReducer(state, action)
}

หากคุณต้องการรีเซ็ตแผนผังสถานะของคุณเพียงชิ้นเดียวคุณสามารถฟังUSER_LOGOUTในตัวลดและจัดการที่นั่น
Andy_D

0

ทำไมคุณไม่ใช้return module.exports.default();)

export default (state = {pending: false, error: null}, action = {}) => {
    switch (action.type) {
        case "RESET_POST":
            return module.exports.default();
        case "SEND_POST_PENDING":
            return {...state, pending: true, error: null};
        // ....
    }
    return state;
}

หมายเหตุ:ตรวจสอบให้แน่ใจว่าคุณได้ตั้งค่าเริ่มต้นของการดำเนินการเป็น{}และคุณตกลงเพราะคุณไม่ต้องการพบข้อผิดพลาดเมื่อคุณตรวจสอบaction.typeภายในคำสั่งสลับ


0

ฉันพบว่าคำตอบที่ยอมรับนั้นใช้งานได้ดีสำหรับฉัน แต่มันทำให้เกิดno-param-reassignข้อผิดพลาดESLint - https://eslint.org/docs/rules/no-param-reassign

นี่คือวิธีที่ฉันจัดการแทนทำให้แน่ใจว่าได้สร้างสำเนาของรัฐ (ซึ่งก็คือในความเข้าใจของฉันสิ่ง Reduxy ที่ต้องทำ ... ):

import { combineReducers } from "redux"
import { routerReducer } from "react-router-redux"
import ws from "reducers/ws"
import session from "reducers/session"
import app from "reducers/app"

const appReducer = combineReducers({
    "routing": routerReducer,
    ws,
    session,
    app
})

export default (state, action) => {
    const stateCopy = action.type === "LOGOUT" ? undefined : { ...state }
    return appReducer(stateCopy, action)
}

แต่บางทีการสร้างสำเนาของรัฐเพียงแค่ส่งไปยังฟังก์ชันลดอื่นที่สร้างสำเนาของที่ซับซ้อนเกินไปเล็กน้อย? สิ่งนี้ไม่ได้อ่านเป็นอย่างดี แต่มีประโยชน์มากกว่า:

export default (state, action) => {
    return appReducer(action.type === "LOGOUT" ? undefined : state, action)
}

0

นอกเหนือจากคำตอบของ Dan Abramov แล้วเราไม่ควรตั้งค่าการกระทำเป็น action = {type: '@@ INIT'} ข้าง state = undefined อย่างชัดเจน ด้วยประเภทการดำเนินการด้านบนตัวลดทุกค่าจะส่งกลับสถานะเริ่มต้น


0

ในเซิร์ฟเวอร์ฉันมีตัวแปรคือ: global.isSsr = true และในแต่ละตัวลดฉันมี const คือ: initialState เพื่อรีเซ็ตข้อมูลใน Store ฉันจะทำสิ่งต่อไปนี้กับ Reducer แต่ละตัว: ตัวอย่างด้วย appReducer.js :

 const initialState = {
    auth: {},
    theme: {},
    sidebar: {},
    lsFanpage: {},
    lsChatApp: {},
    appSelected: {},
};

export default function (state = initialState, action) {
    if (typeof isSsr!=="undefined" && isSsr) { //<== using global.isSsr = true
        state = {...initialState};//<= important "will reset the data every time there is a request from the client to the server"
    }
    switch (action.type) {
        //...other code case here
        default: {
            return state;
        }
    }
}

ในที่สุดเราเตอร์ของเซิร์ฟเวอร์:

router.get('*', (req, res) => {
        store.dispatch({type:'reset-all-blabla'});//<= unlike any action.type // i use Math.random()
        // code ....render ssr here
});

0

วิธีแก้ปัญหาต่อไปนี้ใช้ได้กับฉัน

เป็นครั้งแรกในการเริ่มต้นของโปรแกรมของเรารัฐลดคือสดและใหม่ที่มีค่าเริ่มต้นInitialState

เราต้องเพิ่มการดำเนินการว่าสายบน App โหลด inital จะยังคงมีสถานะเริ่มต้น

ในขณะที่ออกจากระบบของโปรแกรมที่เราสามารถที่เรียบง่ายมอบหมายรัฐเริ่มต้นและลดจะทำงานเช่นเดียวกับที่ใหม่

แอพคอนเทนเนอร์หลัก

  componentDidMount() {   
    this.props.persistReducerState();
  }

แอพลดหลัก

const appReducer = combineReducers({
  user: userStatusReducer,     
  analysis: analysisReducer,
  incentives: incentivesReducer
});

let defaultState = null;
export default (state, action) => {
  switch (action.type) {
    case appActions.ON_APP_LOAD:
      defaultState = defaultState || state;
      break;
    case userLoginActions.USER_LOGOUT:
      state = defaultState;
      return state;
    default:
      break;
  }
  return appReducer(state, action);
};

ในการดำเนินการโทรออกจากระบบสำหรับการรีเซ็ตสถานะ

function* logoutUser(action) {
  try {
    const response = yield call(UserLoginService.logout);
    yield put(LoginActions.logoutSuccess());
  } catch (error) {
    toast.error(error.message, {
      position: toast.POSITION.TOP_RIGHT
    });
  }
}

หวังว่านี่จะช่วยแก้ปัญหาของคุณได้!


0

สำหรับฉันในการรีเซ็ตสถานะเป็นสถานะเริ่มต้นฉันเขียนรหัสต่อไปนี้:

const appReducers = (state, action) =>
   combineReducers({ reducer1, reducer2, user })(
     action.type === "LOGOUT" ? undefined : state,
     action
);

0

สิ่งหนึ่งที่โซลูชันในคำตอบที่ยอมรับไม่ได้ทำคือล้างแคชสำหรับตัวเลือกที่กำหนดพารามิเตอร์ หากคุณมีตัวเลือกดังนี้:

export const selectCounter1 = (state: State) => state.counter1;
export const selectCounter2 = (state: State) => state.counter2;
export const selectTotal = createSelector(
  selectCounter1,
  selectCounter2,
  (counter1, counter2) => counter1 + counter2
);

จากนั้นคุณจะต้องปล่อยพวกเขาเมื่อออกจากระบบเช่นนี้:

selectTotal.release();

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

ตัวอย่างโค้ดมาจากเอกสาร NGRX


0

สำหรับฉันสิ่งที่ทำงานได้ดีที่สุดคือการตั้งinitialStateแทนstate:

  const reducer = createReducer(initialState,
  on(proofActions.cleanAdditionalInsuredState, (state, action) => ({
    ...initialState
  })),

-1

ตัวเลือกอื่นคือ:

store.dispatch({type: '@@redux/INIT'})

'@@redux/INIT'เป็นประเภทการกระทำที่ redux ยื้อโดยอัตโนมัติเมื่อคุณcreateStoreดังนั้นสมมติว่า reducers ของคุณมีค่าเริ่มต้นแล้วสิ่งนี้จะถูกดักจับโดยสิ่งเหล่านั้นและเริ่มต้นสถานะของคุณใหม่ มันอาจถูกพิจารณาว่าเป็นรายละเอียดการใช้งานส่วนตัวของ redux ดังนั้นผู้ซื้อจึงควรระวัง ...


ฉันว่ามันไม่ได้เปลี่ยนสถานะฉันยังลอง @@ INIT ซึ่งแสดงใน ReduxDevtools เป็นการดำเนินการครั้งแรก
Reza

-2

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


1
ถ้าคุณใช้มิดเดิลแวร์ที่ซิงค์ร้านค้ากับ localstorage แล้ววิธีการของคุณไม่ได้ทำงานเลย ...
สป็อค

8
ฉันไม่เข้าใจจริงๆว่าทำไมผู้คนถึงลงมติตอบแบบนี้
Wylliam Judd

ทำไมผู้คนถึงลงคะแนนสิ่งนี้ เมื่อคุณทำสถานะ redux ใหม่ที่มีเนื้อหาว่างเปล่าโดยทั่วไปคุณยังคงมีสถานะก่อนหน้าในหน่วยความจำและคุณสามารถเข้าถึงข้อมูลจากพวกเขาในทางทฤษฎี การรีเฟรชเบราว์เซอร์คือทางออกที่ปลอดภัยที่สุดของคุณ!
Wilhelm Sorban

-3

onLogout() {
  this.props.history.push('/login'); // send user to login page
  window.location.reload(); // refresh the page
}


อธิบาย? @SananSamet
Nikita Beznosikov

เพื่อความชัดเจนฉันไม่ได้ลงคะแนนนี้ แต่ฉันไม่ย่อท้อ หากคุณนำทางไปยังการเข้าสู่ระบบและโหลดใหม่คุณอาจจะออกจากระบบใช่ แต่เพียงเพราะคุณสูญเสียสถานะของคุณ นอกจากว่าคุณใช้ redux-persist ในกรณีนี้คุณจะไม่ออกจากระบบด้วยซ้ำ โดยรวมแล้วฉันเกลียดการดูฟังก์ชั่นเช่นเดียวกับwindow.location.reload();
Sinan Samet

- อย่าเขียนฟังก์ชั่นแบบนี้ - ทำไม - ฉันไม่ชอบ
Nikita Beznosikov

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