मेरे पास निम्न टुकड़ा है:

export const authenticationSlice = createSlice({
    name: 'authentication',
    initialState: {
        isFirstTimeLoading: true,
        signedInUser: null
    },
    reducers: {
        signOut: (state) => {
            state.signedInUser = null
        },
        setUserAfterSignIn: (state, action: PayloadAction<SignInResult>) => {
            // some logic...
            state.signedInUser = {...}
        }
    },
    extraReducers: builder => {
        // Can I subscribe to signedInUser changes here?
    }
})

जब signedInUser में परिवर्तन (setUserAfterSignIn और signOut), extraReducers के भीतर, मैं सदस्यता लेने का कोई तरीका है?

उदाहरण के लिए हर बार setUserAfterSignIn कार्रवाई भेजी जाती है मैं axios में एक इंटरसेप्टर जोड़ना चाहता हूं जो उपयोगकर्ता के एक्सेसटोकन को एक प्रामाणिक शीर्षलेख के रूप में उपयोग करता है।

क्या मैं एंथर स्लाइस से भी इस राज्य की सदस्यता ले सकता हूं? अगर कुछ राज्य एक अलग स्लाइस में signedInUser पर निर्भर करता है?

संपादित करें: यहां वह थंक है जो उपयोगकर्ता में साइन इन करता है, और वह जो साइन आउट करता है

export const { signOut: signOutAction, setUserAfterSignIn: setUserAction } = authenticationSlice.actions

export const signInWithGoogleAccountThunk = createAsyncThunk('sign-in-with-google-account', async (staySignedIn: boolean, thunkAPI) => {
    const state = thunkAPI.getState() as RootState
    state.auth.signedInUser && await thunkAPI.dispatch(signOutThunk())
    const googleAuthUser = await googleClient.signIn()
    const signedInUser = await signInWithGoogleAccountServer({ idToken: googleAuthUser.getAuthResponse().id_token, staySignedIn })
    thunkAPI.dispatch(setUserAction({ data: signedInUser.data, additionalData: { imageUrl: googleAuthUser.getBasicProfile().getImageUrl() } } as SignInResult))
})

export const signInWithLocalAccountThunk = createAsyncThunk('sign-in-with-local-account', async (dto: LocalSignInDto, thunkAPI) => {
    const state = thunkAPI.getState() as RootState
    state.auth.signedInUser && await thunkAPI.dispatch(signOutThunk())
    const user = await signInWithLocalAccountServer(dto)
    thunkAPI.dispatch(setUserAction({ data: user.data } as SignInResult))
})

export const signOutThunk = createAsyncThunk<void, void, { dispatch: AppDispatch }>('sign-out', async (_, thunkAPI) => {
    localStorage.removeItem(POST_SESSION_DATA_KEY)
    sessionStorage.removeItem(POST_SESSION_DATA_KEY)

    const state = thunkAPI.getState() as RootState
    const signedInUser = state.auth.signedInUser

    if (signedInUser?.method === AccountSignInMethod.Google)
        await googleClient.signOut()
    if (signedInUser)
        await Promise.race([signOutServer(), rejectAfter(10_000)])
            .catch(error => console.error('Signing out of server was not successful', error))
            .finally(() => thunkAPI.dispatch(signOutAction()))
})
3
Two Horses 4 सितंबर 2021, 10:17

2 जवाब

सबसे बढ़िया उत्तर

Redux फ्लक्स आर्किटेक्चर लागू करता है।

Flux architecutre

यह संरचना हमें अपने एप्लिकेशन के बारे में आसानी से तर्क करने की अनुमति देती है जो कार्यात्मक प्रतिक्रियाशील प्रोग्रामिंग की याद दिलाता है, या अधिक विशेष रूप से डेटा-फ्लो प्रोग्रामिंग या फ्लो-आधारित प्रोग्रामिंग, जहां डेटा एक ही दिशा में एप्लिकेशन के माध्यम से बहता है - कोई दो नहीं हैं- रास्ता बंधन।

रेड्यूसर एक दूसरे पर निर्भर नहीं होना चाहिए, क्योंकि रेडक्स एक विशिष्ट क्रम सुनिश्चित नहीं करता है जिसमें उन्हें निष्पादित किया जाता है। आप combineReducer का उपयोग करके इसे हल कर सकते हैं। आप सुनिश्चित नहीं हो सकते कि extraReducers को setUserAfterSignIn रिड्यूसर के बाद निष्पादित किया जाता है।

आपके पास विकल्प हैं:

  1. वह कोड डालें जो axios इंटरसेप्टर को setUserAfterSignIn रिड्यूसर में अपडेट करता है।

     setUserAfterSignIn: (state, action: PayloadAction<SignInResult>) => {
         // some logic...
         state.signedInUser = {...}
         // update axios
     }
    
  2. एक्सिस इंटरसेप्टर बनाएं और इसे एक सप्लायर पास करें जो स्टोर से जुड़ा हो। इस तरह आप आसानी से टोकन की आपूर्ति करने के तरीके को बदल सकते हैं।

     const tokenSupplier = () => store.getState().signedInUser;
    
     // ...
    
     axios.interceptors.request.use(function (config) {
         const token = tokenSupplier();
         config.headers.Authorization =  token;
    
         return config;
     });
    
  3. दो रेड्यूसर फ़ंक्शन निकालें और उनका ऑर्डर सुनिश्चित करें।

    function signInUser(state, action) {
        state.signedInUser = {...}
    }
    
    function onUserSignedIn(state, action) {
        // update axios interceptor
    }
    
    // ....
    // ensure their order in the redux reducer.
    
    setUserAfterSignIn: (state, action: PayloadAction<SignInResult>) => {
         signInUser(state, action);
         onUserSignedIn(state, action)
    }
    

संपादित करें

इस आर्किटेक्चर को देखते हुए, मेरे पास क्या विकल्प हैं यदि मेरे पास एक और टुकड़ा है जिसे साइन इनयूसर बदल जाने पर प्रतिक्रिया करने की आवश्यकता है?

मुझे लगता है कि आपको जवाब पसंद नहीं आएगा। मैं कुछ समय पहले इसी मुद्दे से जूझ रहा था।

एक और टुकड़ा स्टोर में एक स्वतंत्र हिस्सा है। आप अतिरिक्त रेड्यूसर जोड़ सकते हैं जो अन्य स्लाइस से क्रियाओं को सुन सकते हैं, लेकिन आप यह सुनिश्चित नहीं कर सकते कि दूसरे स्लाइस के रेड्यूसर ने पहले ही राज्य को अपडेट कर दिया है।

मान लें कि आपके पास एक स्लाइस A और एक रेड्यूसर RA और एक स्लाइस B है जिसमें एक रेड्यूसर RB है। यदि राज्य B A पर निर्भर करता है, तो इसका मतलब है कि जब भी A बदलता है, तो रिड्यूसर RB को निष्पादित करना चाहिए।

आप RA RB को कॉल कर सकते हैं, लेकिन यह RB पर निर्भरता का परिचय देता है। यह अच्छा होगा यदि RA { type: "stateAChanged", payload: stateA} जैसी कार्रवाई भेज सके ताकि अन्य स्लाइस उस क्रिया को सुन सकें, लेकिन रेड्यूसर कार्रवाई नहीं भेज सकते। आप एक मिडलवेयर लागू कर सकते हैं जो डिस्पैचर के साथ क्रियाओं को बढ़ाता है। उदा.

function augmentAction(store, action) {
  action.dispatch = (a) => {
     store.dispatch(a)
  }
  store.dispatch(action)
}

ताकि रेड्यूसर कार्रवाई भेज सकें।

setUserAfterSignIn: (state, action: PayloadAction<SignInResult>) => {
    // some logic...
    state.signedInUser = {...}
    action.dispatch({type : "userSignedIn": payload: {...state}})
}

लेकिन यह दृष्टिकोण एक मानक दृष्टिकोण नहीं है और यदि आप इसका अत्यधिक उपयोग करते हैं, तो आप ऐसे चक्र पेश कर सकते हैं जो प्रेषण में अंतहीन लूप की ओर ले जाते हैं।

अलग-अलग स्लाइस का उपयोग करने के बजाय कुछ अलग-अलग स्टोर का उपयोग करते हैं और स्टोर की सदस्यता का उपयोग करके उन्हें कनेक्ट करते हैं। यह एक आधिकारिक एपी है, लेकिन यदि आप पर्याप्त ध्यान नहीं देते हैं तो यह लूप भी पेश कर सकता है।

तो अंत में सबसे आसान तरीका है RB को RA से कॉल करना। आप उनके बीच निर्भरता को थोड़ा उलट कर प्रबंधित कर सकते हैं। उदा.

const onUserSignedIn = (token) => someOtherReducer(state, { type: "updateAxios", payload: token});

setUserAfterSignIn: (state, action: PayloadAction<SignInResult>) => {
    // some logic...
    state.signedInUser = {...}
    onUserSignedIn(state.signedInUser.token)
}

अब आप onUserSignedIn कॉलबैक को परीक्षणों में या अन्य पंजीकृत कॉलबैक को कॉल करने वाले समग्र फ़ंक्शन के साथ बदल सकते हैं।

2
René Link 4 सितंबर 2021, 10:09

हां, एक और टुकड़ा विभिन्न क्रिया प्रकारों को सुन सकता है और अपने स्वयं के राज्य ऑब्जेक्ट पर एक्शन पेलोड (या मेटा डेटा) लागू कर सकता है।

लेकिन सावधान रहें कि आप अपने स्टोर के विभिन्न हिस्सों में फैले हुए प्रतिबिंबित या व्युत्पन्न अवस्था को समाप्त न करें।

export const signInThunk = createAsyncThunk('signIn', async (_: void, thunkAPI) => {
    const authUser = await authClient.signIn()
    return authUser
})


export const authenticationSlice = createSlice({
    name: 'authentication',
    initialState: {
        isFirstTimeLoading: true,
        signedInUser: null
    },
    reducers: {},
    extraReducers: builder => {
      builder.addCase(signInThunk.fulfilled, (state, action) => {
          state.signedInUser = action.payload
      })
    }
})

export const anotherSlice = createSlice({
    name: 'another',
    initialState: {},
    reducers: {},
    extraReducers: builder => {
      builder.addCase(signInThunk.fulfilled, (state, action) => {
          // do something with the action
      })
    }
})


0
ksav 5 सितंबर 2021, 01:34