Làm thế nào để thiết lập lại trạng thái của một cửa hàng Redux?


455

Tôi đang sử dụng Redux để quản lý nhà nước.
Làm cách nào để đặt lại cửa hàng về trạng thái ban đầu?

Ví dụ: giả sử tôi có hai tài khoản người dùng ( u1u2).
Hãy tưởng tượng chuỗi các sự kiện sau đây:

  1. Người dùng u1đăng nhập vào ứng dụng và làm một cái gì đó, vì vậy chúng tôi lưu trữ một số dữ liệu trong cửa hàng.

  2. Người dùng u1đăng xuất.

  3. Người dùng u2đăng nhập vào ứng dụng mà không cần làm mới trình duyệt.

Tại thời điểm này, dữ liệu được lưu trong bộ nhớ cache sẽ được liên kết u1và tôi muốn dọn sạch nó.

Làm cách nào tôi có thể đặt lại cửa hàng Redux về trạng thái ban đầu khi người dùng đầu tiên đăng xuất?


8
Thay vào đó, có lẽ tốt hơn để xóa trạng thái khi đăng xuất (từ góc độ bảo mật)
Clarkie

Câu trả lời:


1033

Một cách để làm điều đó là viết một trình giảm gốc trong ứng dụng của bạn.

Bộ giảm tốc gốc thường sẽ ủy thác xử lý hành động cho bộ giảm tốc được tạo bởi combineReducers(). Tuy nhiên, bất cứ khi nào nó nhận được USER_LOGOUThành động, nó sẽ trả lại trạng thái ban đầu.

Ví dụ: nếu trình giảm gốc của bạn trông như thế này:

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

Bạn có thể đổi tên nó thành appReducervà viết một rootReducerủy nhiệm mới cho nó:

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

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

Bây giờ chúng ta chỉ cần dạy cái mới rootReducerđể trả về trạng thái ban đầu sau khi USER_LOGOUThành động. Như chúng ta biết, các bộ giảm tốc được cho là trả về trạng thái ban đầu khi chúng được gọi với undefinedtư cách là đối số đầu tiên, bất kể hành động. Chúng ta hãy sử dụng thực tế này để loại bỏ một cách có điều kiện statekhi chúng ta chuyển nó tới appReducer:

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

  return appReducer(state, action)
}

Bây giờ, bất cứ khi nào USER_LOGOUTcháy, tất cả các bộ giảm tốc sẽ được khởi tạo lại. Họ cũng có thể trả lại một cái gì đó khác với ban đầu nếu họ muốn vì họ cũng có thể kiểm tra action.type.

Để nhắc lại, mã hoàn toàn mới trông như thế này:

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

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

  return appReducer(state, action)
}

Lưu ý rằng tôi không làm thay đổi trạng thái ở đây, tôi chỉ đơn thuần gán lại tham chiếu của một biến cục bộ được gọi statetrước khi chuyển nó sang hàm khác. Đột biến một đối tượng trạng thái sẽ là vi phạm các nguyên tắc Redux.

Trong trường hợp bạn đang sử dụng redux-contin , bạn cũng có thể cần phải làm sạch bộ nhớ của mình. Redux-contin giữ một bản sao trạng thái của bạn trong một công cụ lưu trữ và bản sao trạng thái sẽ được tải từ đó khi làm mới.

Trước tiên, bạn cần nhập công cụ lưu trữ thích hợp và sau đó, phân tích trạng thái trước khi cài đặt undefinedvà làm sạch từng khóa trạng thái lưu trữ.

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
Tôi tò mò Dan, bạn cũng có thể làm một cái gì đó như thế này trong bộ giảm tốc của bạn. với CLEAR_DATA là hành động. case 'CLEAR_DATA': return initialState
HussienK

6
@HussienK sẽ hoạt động nhưng không ở trạng thái cho mỗi bộ giảm.
Cory Danielson

12
Đây là phiên bản mà bạn kết hợp linh hoạt các bộ giảm tốc trong trường hợp bạn sử dụng bộ giảm tốc không đồng bộ:export const createRootReducer = asyncReducers => { const appReducer = combineReducers({ myReducer ...asyncReducers }); return (state, action) => { if (action.type === 'LOGOUT_USER') { state = undefined; } return appReducer(state, action); } };
Ivo Sabev

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

3
Liệu cách tiếp cận này hoàn toàn rõ ràng nhà nước và tất cả lịch sử của nó? Tôi đang suy nghĩ từ góc độ bảo mật: Nếu điều này đã được thực hiện, một khi USER_LOGOUThành động đã được thực hiện, liệu có thể có được dữ liệu trạng thái từ trước đó không? (ví dụ: thông qua devtools)
AlexKempton

81

Tôi muốn chỉ ra rằng nhận xét được chấp nhận bởi Dan Abramov là chính xác trừ khi chúng tôi gặp phải một vấn đề lạ khi sử dụng gói phản ứng-bộ định tuyến-redux cùng với phương pháp này. Cách khắc phục của chúng tôi là không đặt trạng thái thành undefinednhưng vẫn sử dụng bộ giảm tốc định tuyến hiện tại. Vì vậy, tôi sẽ đề nghị thực hiện giải pháp dưới đây nếu bạn đang sử dụng gói này

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

19
Tôi nghĩ rằng điều đáng nói ở đây là bạn có thể không muốn xóa toàn bộ cây trạng thái khi đăng xuất - cách tiếp cận hoạt động tốt như nhau trong trình giảm gốc của bất kỳ cây con nào, và vì vậy có thể rõ ràng hơn khi chỉ áp dụng kỹ thuật này ở trình giảm gốc cây con (s), bạn làm muốn xóa, chứ không phải là chọn ra 'đặc biệt' trẻ em không rõ ràng tại giảm tốc gốc của toàn bộ cây, như thế này
davnicwil

1
Tôi nghĩ rằng tôi đang gặp phải vấn đề này mà bạn đang đề cập đến ngay bây giờ, (khi đăng xuất, nó sẽ đặt tuyến đường đi đúng đường nhưng một thành phần hoàn toàn khác sẽ tải) tôi đã triển khai một cái gì đó tương tự như của bạn để khắc phục nó, nhưng tôi nghĩ có gì đó với js bất biến đang massge này lên. Cuối cùng tôi đã tạo ra một bộ giảm tốc cha mẹ có hành động RESET-STATE và tôi thừa hưởng từ bộ giảm tốc đó để tránh chạm hoàn toàn vào việc định tuyến
Neta Meta

Đã gặp vấn đề tương tự, điều này đã khắc phục nó. Cảm ơn.
Lloyd Watkin

3
Lưu ý rằng với bộ định tuyến phản ứng-redux, thuộc tính routervà KHÔNGrounting
Mrchief

2
@Mrchief nó phụ thuộc vào những gì bạn xác định nó như trong combineReducers()..... nếu bạn có combineReducers({routing: routingReducer})nó sẽ được mô tả trong câu trả lời
Ben Lonsdale

40

Xác định một hành động:

const RESET_ACTION = {
  type: "RESET"
}

Sau đó, trong mỗi bộ giảm tốc của bạn giả sử bạn đang sử dụng switchhoặc if-elseđể xử lý nhiều hành động thông qua mỗi bộ giảm tốc. Tôi sẽ lấy trường hợp cho a 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; 
  }
}

Bằng cách này bất cứ khi nào bạn gọi RESEThành động, bộ giảm tốc sẽ cập nhật cửa hàng với trạng thái mặc định.

Bây giờ, để đăng xuất, bạn có thể xử lý như sau:

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

Mỗi khi người dùng đăng nhập, không cần làm mới trình duyệt. Cửa hàng sẽ luôn được mặc định.

store.dispatch(RESET_ACTION)chỉ cần xây dựng ý tưởng. Bạn rất có thể sẽ có một người tạo hành động cho mục đích này. Một cách tốt hơn nhiều sẽ là bạn có một LOGOUT_ACTION.

Một khi bạn gửi điều này LOGOUT_ACTION. Một phần mềm trung gian tùy chỉnh sau đó có thể chặn hành động này, với Redux-Saga hoặc Redux-Thunk. Cả hai cách, tuy nhiên, bạn có thể gửi một hành động khác 'RESET'. Bằng cách này, cửa hàng đăng xuất và đặt lại sẽ diễn ra đồng bộ và cửa hàng của bạn sẽ sẵn sàng cho đăng nhập người dùng khác.


1
tôi cảm thấy như đây là cách tiếp cận tốt hơn là chỉ đặt trạng thái undefinedthích trong câu trả lời khác. khi ứng dụng của bạn đang mong đợi một cây trạng thái và undefinedthay vào đó bạn cung cấp cho nó , sẽ chỉ có nhiều lỗi và vấn đề đau đầu hơn là một cây trống.
worc

3
@worc Trạng thái sẽ không thực sự không được xác định, vì các bộ giảm trở lại initState khi chúng nhận được trạng thái không xác định
Guillaume

3
@worc nghĩ rằng với cách tiếp cận này, mỗi khi có ai tạo ra bộ giảm tốc mới, bạn sẽ phải nhớ thêm trường hợp đặt lại.
Francute

1
Tôi chắc chắn đã thay đổi suy nghĩ của mình về điều này, vì cả hai lý do đó, cộng với ý tưởng rằng RESET_ACTION là một hành động . Vì vậy, nó không thực sự thuộc về bộ giảm tốc để bắt đầu.
worc

1
Đây chắc chắn là phương pháp đúng đắn. Đặt trạng thái thành bất cứ điều gì ngoại trừ Trạng thái ban đầu chỉ là vấn đề rắc rối
Sebastian Serrano

15

Chỉ cần một câu trả lời đơn giản cho câu trả lời tốt nhất:

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


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

Điều này đang làm việc cảm ơn bạn, tôi đã từ câu trả lời của Dan nhưng tôi không thể tìm ra nó.
Aljohn Yamaro

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

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

   return state
 }

Bạn cũng có thể thực hiện một hành động được xử lý bởi tất cả hoặc một số bộ giảm tốc mà bạn muốn đặt lại về cửa hàng ban đầu. Một hành động có thể kích hoạt thiết lập lại toàn bộ trạng thái của bạn hoặc chỉ là một phần của hành động đó có vẻ phù hợp với bạn. Tôi tin rằng đây là cách đơn giản nhất và dễ kiểm soát nhất để làm việc này.


10

Với Redux nếu đã áp dụng giải pháp sau, giả sử tôi đã đặt initState trong tất cả các bộ giảm tốc của mình (ví dụ: {user: {name, email}}). Trong nhiều thành phần tôi kiểm tra các thuộc tính lồng nhau này, vì vậy với cách khắc phục này, tôi ngăn các phương thức kết xuất của mình bị hỏng trong các điều kiện thuộc tính được ghép nối (ví dụ: state.user.email, sẽ ném lỗi người dùng không xác định nếu các giải pháp được đề cập ở trên).

const appReducer = combineReducers({
  tabs,
  user
})

const initialState = appReducer({}, {})

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

  return appReducer(state, action)
}

7

CẬP NHẬT NGRX4

Nếu bạn đang di chuyển sang NGRX 4, bạn có thể nhận thấy từ hướng dẫn di chuyển rằng phương pháp rootreducer để kết hợp các bộ giảm tốc của bạn đã được thay thế bằng phương pháp ActionReducerMap. Lúc đầu, cách làm mới này có thể khiến việc đặt lại trạng thái trở thành một thách thức. Nó thực sự là đơn giản, nhưng cách làm này đã thay đổi.

Giải pháp này được lấy cảm hứng từ phần API giảm tốc của các tài liệu Github NGRX4.

Trước tiên, giả sử bạn đang kết hợp các bộ giảm tốc của mình như thế này bằng tùy chọn ActionReducerMap mới của 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
}

Bây giờ, giả sử bạn muốn đặt lại trạng thái từ trong 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 { }

`

Và đó về cơ bản là một cách để đạt được ảnh hưởng tương tự với NGRX 4.


5

Kết hợp các cách tiếp cận của Dan, Ryan và Rob, để giải thích cho việc giữ routertrạng thái và khởi tạo mọi thứ khác trong cây trạng thái, tôi đã kết thúc việc này:

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

4

Tôi đã tạo một thành phần để cung cấp cho Redux khả năng thiết lập lại trạng thái, bạn chỉ cần sử dụng thành phần này để nâng cao cửa hàng của mình và gửi một hành động cụ thể để kích hoạt thiết lập lại. Ý nghĩ thực hiện giống như những gì @Dan Abramov nói.

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


4

Tôi đã tạo ra hành động để xóa trạng thái. Vì vậy, khi tôi gửi một trình tạo hành động đăng xuất, tôi cũng gửi các hành động để xóa trạng thái.

Hành động hồ sơ người dùng

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

Người tạo hành động đăng xuất

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

Giảm tốc

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
  }
};

Tôi không chắc chắn nếu điều này là tối ưu?


2

Nếu bạn đang sử dụng các hành động chuyển hướng , đây là một cách giải quyết nhanh bằng cách sử dụng HOF ( Chức năng bậc cao hơn ) cho handleActions.

import { handleActions } from 'redux-actions';

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

Và sau đó sử dụng handleActionsExthay vì ban đầu handleActionsđể xử lý giảm tốc.

Câu trả lời của Dan cho một ý tưởng tuyệt vời về vấn đề này, nhưng nó không giải quyết tốt cho tôi, bởi vì tôi đang sử dụng redux-persist.
Khi được sử dụng redux-persist, undefinedtrạng thái chuyển qua đơn giản không kích hoạt hành vi dai dẳng, vì vậy tôi biết rằng tôi phải xóa thủ công khỏi mục lưu trữ (Do đó, React Native trong trường hợp của tôi AsyncStorage).

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

hoặc là

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

cũng không làm việc cho tôi - họ chỉ mắng tôi. (ví dụ: phàn nàn như "Khóa bất ngờ _persist ..." )

Sau đó, tôi đột nhiên suy nghĩ tất cả những gì tôi muốn chỉ là làm cho mỗi bộ giảm tốc riêng lẻ trở lại trạng thái ban đầu của riêng họ khi RESETgặp loại hành động. Bằng cách đó, kiên trì được xử lý một cách tự nhiên. Rõ ràng là không có chức năng tiện ích ở trên ( handleActionsEx), mã của tôi sẽ không nhìn DRY (mặc dù nó chỉ là một lớp lót RESET: () => initialState, nhưng tôi không thể chịu đựng được vì tôi thích lập trình siêu dữ liệu.


2

Các giải pháp sau đây làm việc cho tôi.

Tôi đã thêm chức năng thiết lập lại trạng thái cho bộ giảm meta. Chìa khóa là sử dụng

return reducer(undefined, action);

để đặt tất cả các bộ giảm tốc về trạng thái ban đầu. undefinedThay vào đó, việc quay lại gây ra lỗi do thực tế là cấu trúc của cửa hàng đã bị phá hủy.

/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

Từ góc độ an ninh, điều an toàn nhất để làm khi đăng nhập một ra sử dụng là để thiết lập lại tất cả các bang dai dẳng (cookie ex, localStorage, IndexedDB, Web SQL, vv) và làm mới cứng của trang sử dụng window.location.reload(). Có thể một nhà phát triển cẩu thả đã vô tình hoặc cố ý lưu trữ một số dữ liệu nhạy cảm trên windowDOM, v.v. Thổi bay mọi trạng thái liên tục và làm mới trình duyệt là cách duy nhất để đảm bảo không có thông tin nào từ người dùng trước bị rò rỉ cho người dùng tiếp theo.

(Tất nhiên, là người dùng trên máy tính dùng chung, bạn nên sử dụng chế độ "duyệt riêng tư", tự đóng cửa sổ trình duyệt, sử dụng chức năng "xóa dữ liệu duyệt web", v.v., nhưng là nhà phát triển, chúng tôi không thể luôn mong muốn mọi người luôn luôn siêng năng đó)


1
Tại sao mọi người lại đánh giá thấp điều này? Khi bạn thực hiện trạng thái redux mới, với nội dung trống, về cơ bản bạn vẫn có các trạng thái trước đó trong bộ nhớ và về mặt lý thuyết bạn có thể truy cập dữ liệu từ chúng. Làm mới trình duyệt LÀ đặt cược an toàn nhất của bạn!
Wilhelm Sorban

2

Cách giải quyết của tôi khi làm việc với bản thảo, được xây dựng dựa trên câu trả lời của Dan (cách viết lại khiến cho không thể chuyển undefinedsang bộ giảm tốc làm đối số đầu tiên, vì vậy tôi lưu trữ trạng thái gốc ban đầu trong một hằng số):

// 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

Câu trả lời được chấp nhận đã giúp tôi giải quyết trường hợp của mình. Tuy nhiên, tôi đã gặp trường hợp không phải toàn bộ tiểu bang phải được xóa. Vì vậy - tôi đã làm theo cách này:

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;

Hy vọng điều này sẽ giúp người khác :)


Điều gì xảy ra nếu tôi có hơn 10 trạng thái nhưng tôi muốn đặt lại trạng thái chỉ là một bộ giảm?
Paul

1

Chỉ cần một phần mở rộng cho câu trả lời @ dan-abramov , đôi khi chúng ta có thể cần giữ lại một số khóa nhất định khỏi bị đặt lại.

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

Một tùy chọn nhanh chóng và dễ dàng phù hợp với tôi là sử dụng redux-reset . Điều này rất đơn giản và cũng có một số tùy chọn nâng cao, cho các ứng dụng lớn hơn.

Thiết lập trong cửa hàng tạo

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)

Gửi 'thiết lập lại' trong chức năng đăng xuất của bạn

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

Hi vọng điêu nay co ich


1

Tôi cố gắng giữ Redux không tham chiếu đến cùng một biến của trạng thái ban đầu:

// 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;
  }
};

Ý tưởng viết trạng thái mặc định là một hàm thực sự đã lưu trong ngày ở đây. Cảm ơn bạn 🙏
Chris Paul

0

Cách tiếp cận này rất đúng: Hủy cấu trúc bất kỳ trạng thái "TÊN" cụ thể nào để bỏ qua và giữ cho người khác.

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

Nếu bạn chỉ cần thiết lập lại một phần của cây trạng thái của mình, bạn cũng có thể lắng nghe USER_LOGOUTtrong bộ giảm tốc đó và xử lý nó ở đó.
Andy_D

0

tại sao bạn không sử dụng 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;
}

Lưu ý: đảm bảo bạn đặt giá trị mặc định hành động thành {}và bạn vẫn ổn vì bạn không muốn gặp lỗi khi kiểm tra action.typebên trong câu lệnh chuyển đổi.


0

Tôi thấy rằng câu trả lời được chấp nhận hoạt động tốt với tôi, nhưng nó đã gây ra no-param-reassignlỗi ESLint - https://eslint.org/docs/rules/no-param-reassign

Đây là cách tôi xử lý nó thay vào đó, đảm bảo tạo một bản sao của trạng thái (theo cách hiểu của tôi, điều Reduxy phải làm ...):

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)
}

Nhưng có lẽ việc tạo một bản sao của trạng thái để chuyển nó vào một hàm giảm tốc khác tạo ra một bản sao của nó là quá phức tạp? Điều này không đọc là độc đáo, nhưng là quan trọng hơn:

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

0

Ngoài câu trả lời của Dan Abramov, chúng ta không nên đặt rõ ràng hành động là hành động = {type: '@@ INIT'} cùng với trạng thái = không xác định. Với loại hành động trên, mọi bộ giảm đều trả về trạng thái ban đầu.


0

trong máy chủ, tôi có một biến là: global.isSsr = true và trong mỗi bộ giảm tốc, tôi có một const là: initState Để đặt lại dữ liệu trong Store, tôi thực hiện như sau với mỗi Reducer: ví dụ với 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;
        }
    }
}

cuối cùng trên bộ định tuyến của máy chủ:

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

0

Các giải pháp sau đây làm việc cho tôi.

Đầu tiên khi bắt đầu ứng dụng của chúng tôi, trạng thái bộ giảm là mớimới với InitialState mặc định .

Chúng ta phải thêm một hành động yêu cầu tải APP trong ứng dụng để duy trì trạng thái mặc định .

Trong khi đăng xuất khỏi ứng dụng chúng ta có thể đơn giản phân công lại các trạng thái mặc định và giảm sẽ làm việc giống như mới .

Container ứng dụng chính

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

Giảm tốc APP chính

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);
};

Khi đăng xuất hành động gọi để đặt lại trạng thái

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
    });
  }
}

Hy vọng điều này sẽ giải quyết vấn đề của bạn!


0

Để tôi đặt lại trạng thái về trạng thái ban đầu, tôi đã viết đoạn mã sau:

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

0

Một điều mà giải pháp trong câu trả lời được chấp nhận không làm là xóa bộ đệm cho các bộ chọn tham số. Nếu bạn có một bộ chọn như thế này:

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

Sau đó, bạn sẽ phải phát hành chúng khi đăng xuất như thế này:

selectTotal.release();

Mặt khác, giá trị ghi nhớ cho cuộc gọi cuối cùng của bộ chọn và các giá trị của các tham số cuối cùng sẽ vẫn còn trong bộ nhớ.

Mẫu mã là từ các tài liệu ngrx .


0

Đối với tôi, điều tốt nhất là đặt initialStatethay vì state:

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

-1

Một lựa chọn khác là:

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

'@@redux/INIT'là loại hành động mà redux gửi tự động khi bạn createStore, vì vậy giả sử tất cả các bộ giảm tốc của bạn đã có mặc định, điều này sẽ bị bắt bởi những cái đó và bắt đầu trạng thái của bạn mới. Tuy nhiên, nó có thể được coi là một chi tiết triển khai riêng tư của redux, vì vậy người mua hãy cẩn thận ...


Tôi đã làm điều đó không thay đổi trạng thái, tôi cũng đã thử @@ INIT, được hiển thị trong ReduxDevtools như hành động đầu tiên
Reza

-2

Đơn giản chỉ cần có liên kết đăng xuất rõ ràng phiên và làm mới trang. Không có mã bổ sung cần thiết cho cửa hàng của bạn. Bất cứ khi nào bạn muốn thiết lập lại hoàn toàn trạng thái làm mới trang là một cách đơn giản và dễ lặp lại để xử lý nó.


1
Điều gì nếu bạn sử dụng một phần mềm trung gian đồng bộ hóa cửa hàng với lưu trữ cục bộ? Sau đó, cách tiếp cận của bạn hoàn toàn không hiệu quả ...
Spock

8
Tôi thực sự không hiểu tại sao mọi người lại hạ thấp câu trả lời như thế này.
Wylliam Judd 7/2/18

Tại sao mọi người lại đánh giá thấp điều này? Khi bạn thực hiện trạng thái redux mới, với nội dung trống, về cơ bản bạn vẫn có các trạng thái trước đó trong bộ nhớ và về mặt lý thuyết bạn có thể truy cập dữ liệu từ chúng. Làm mới trình duyệt LÀ đặt cược an toàn nhất của bạn!
Wilhelm Sorban

-3

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


giải thích? @SinanSamet
Nikita Beznosikov

Để rõ ràng tôi đã không bỏ phiếu này. Nhưng tôi không khuyến khích nó. Nếu bạn điều hướng để đăng nhập và tải lại, bạn có thể sẽ đăng xuất. Nhưng chỉ vì bạn mất trạng thái của bạn. Trừ khi bạn sử dụng redux-contin trong trường hợp bạn thậm chí không đăng xuất. Nhìn chung, tôi chỉ ghét nhìn thấy các chức năng nhưwindow.location.reload();
Sinan Samet

- Không viết các chức năng như thế này. - Tại sao? - Tôi không thích nó.
Nikita Beznosikov

1
Tải lại trang khác với đặt lại trạng thái cửa hàng. Ngoài ra, bạn có thể ở trong một môi trường không có window, ví dụ:
Max
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.