I am trying to integrate redux-simple-auth in my existing reudx API. My old implementation used the native fetch and I added headers etc.. This new module provides a fetch replacement however it returns a redux action and am having a bit of a struggle figuring out how to set things up.
My store:
function configureStore (initialState) {
const middlewares = [
authMiddleware, // This is from rediux-simple-auth
apiMiddleware, // My own middleware
thunk
]
const store = createStore(rootReducer, initialState, compose(
applyMiddleware(...middlewares), getInitialAuthState({ storage }))
)
}
My middleware simplified:
import { fetch } from 'redux-simple-auth'
export default store => next => action => {
...
next(my start action...)
return store.dispatch(fetch(API_ROOT + endpoint, { method }))
.then(response => {
next(my success/fail action...)
})
}
When I run this I can see my start and fail actions in redux inspector but not the fetch one (which does trigger a FETCH one)
If I call next instead of store.dispatch then it works in the sense that it tiggers the action but it does not return a promise I cannot get results.
How can I fix this flow?
next is not dispatch. Try this:
export default ({dispatch}) => next => action => ...
Use dispatch to dispatch your new action and use next(action) to pass the original action to the next middleware.
Related
I created a react app using create-react-app and configured a redux store with reducers. I also added firebase and my project works fine. The components can trigger an action that fetches a collection from firestore, and it in return, updates the redux store.
What is the best way to integrate firebase and redux store?
The way I am currently doing it, is to have a separate action that triggers the fetch/delete/onSnapshot from firebase, and handing a reference to dispatch so that firebase function can take its time executing the command, then it can call dispatch with an action that updates the store.
But I wanted all of my actions in a single file, for a better (separation of concerns). Therefor, firebase can call dispatch but the action creator lives in my actions.js file. This way, I can later decide to change the action names in a single file, if I decided to do that.
The problem with this approach, is I will require a separate action to trigger the async function with firebase, and another action creator for when the promise is fulfilled.
What is a better approach to what I am doing?
store.js
const rootReducer = combineReducers({
cards: cardsReducer,
});
const store = createStore( rootReducer , {}, applyMiddleware(thunk));
export default store;
myFirebase.js
// I need this to be called from an action in actions.js
// therefor, I am exporting it, and also, I am handing it dispatch
// so it will call store.dispatch once data is ready
export const fetchCardsFromFirebase = async (dispatch) => {
const cardsCollection = collection(db, "cards");
const cardsSnapshot = await getDocs(roomsCollection);
const cards = roomsSnapshot.docs.map(doc => ({ ...doc.data(), id: doc.id }));
// here I can either explicitly dispatch an action
/*
dispatch({
type: CARDS_FETCHED //this constant string will have to be imported
payload: cards
});
*/
// or I can let an action in actions.js do the above:
dispatch(cardsFetched(rooms)); //this function is imported from actions.js
}
actions.js
import { FETCH_CARDS , CARDS_FETCHED } from "./types";
import { fetchCardsFromFirebase } from "../myFirebase";
export const fetchCards = () => async (dispatch) => {
fetchCardsFromFirebase(dispatch); // give firebase access to dispatch
dispatch({
type: FETCH_CARDS,
payload: {message: "fetching cards... please wait"}
});
};
const cardsFetched = (cards) => ({
action: CARDS_FETCHED,
payload: cards
});
Generally, this is a very old style of Redux - modern Redux does not use switch..case reducers or ACTION_TYPES and switching to modern Redux will proably already save you 50% of your code.
That said, the official Redux Toolkit (RTK) also comes with RTK-Query, which is a data caching abstraction that should also work fine with firebase and will generate reducers, actions and even hooks automatically for you. (Hint: with firebase you will need to use queryFn). That would save you a lot more code as well.
I would recommend you to follow the official Redux Tutorial which first shows modern Redux and in later chapters goes into RTK Query.
I would like to create some middleware to check a tokens expires_at field refreshing the token if necessary and updating the state for the token fields.
I have used the redux toolkit to create the redux functionality, making use of slices. I have a slice that will update the state with token data and this works on the callback for the initial token.
When creating the middleware I can not get the slice to be called and therefore the state remains unchanged.
As a simple spike without an api call:
import { useSelector, useDispatch } from 'react-redux';
import { setTokens } from '../features/tokens/tokenSlice';
const refresher = () => ({ dispatch, getState }) => (next) => (action) => {
const exp_at = useSelector((state) => state.tokens.expires_at);
if(hasBreachedThreshold(exp_at)){
dispatch = useDispatch();
dispatch(
setTokens({
access_token: 'new token',
expires_at: 637423776000000000, -- hard coded date way in the future.
})
}
... else carry on.
);
I would expect when the middleware is called, on the first pass hasBreachedThreshold() returns true and the dispatch method would call the slice reducer and update the state. Any further runs would pass over as hasBreachedThreshold() would return false - for a while anyway.
What is happening though is that the hasBreachThreshold always returns false because the state is never updated, causing an indefinite loop.
The middleware is configured correctly and is called.
The expires_at value is extracted from the state.
hasBreachThreshold() is tested thoroughly and behaves correctly.
Being fairly new to React / Redux I expect my understanding of how and when to use dispatch is wrong. I assumed I could use dispatch similarly to how I do in my components, is this not the case? or am I going about this totally the wrong way?
When writing a middleware you have already the dispatch function and Redux store available through the function params:
// Use `dispatch` & `getState`
const refresher = () => ({ dispatch, getState }) => (next) => (action) => {
const exp_at = getState().tokens.expires_at;
if(hasBreachedThreshold(exp_at)){
dispatch(
setTokens({
access_token: 'new token',
expires_at: 637423776000000000, -- hard coded date way in the future.
})
}
);
Also you have essential mistake of when to use React hooks, refer to Rules of Hooks.
Hooks are not available in context of Redux middleware.
So I am currently new with redux and stuck in the middleware part. I need to know how these two codes interact with each other.
My action creator:
import jsonPlaceHolder from '../APis/jsonPlaceHolder';
export const fetchPosts= ()=>{
return async (dispatch)=>{
const response = await jsonPlaceHolder.get('/posts');
dispatch({type:'FETCH_POSTS',payload: response});
};
};
redux-thunk code:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => (next) => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
Plain redux only works with sync actions. Redux thunk gives you the ability to work with async actions (to dispatch multiple actions from a single action creator, for async actions that is usually the REQUEST/RESPONSE/ERROR action). Middleware is something that stands between you dispatching the action and reducer updating the store. Since redux only works with plain objects, to use a action creator (like fetchPosts) you need something (redux-thunk here). It simply injects the dispatch parameter (and getState that gives you the ability to get the current state if your action creator depends on it).
The next(action) inside middleware is the method that propagates your action object to the next middleware(or if it is the last one to your reducer). Redux-thunk checks if the thing you dispatched is a function(since we said that redux can only work with plain objects), and if it is a function it just injects the above mentioned parameters.
So it is basically:
dispatch(fetchPosts()) -> redux-thunk-middleware -> it is function so
lt's call it with injected dispatch/getState (this will not be propagated to the reducer) ->
dispatch({type:'FETCH_POSTS',payload: response}) ->
redux-thunk-middleware -> not a function, let it through -> reducer ->
update state
Hope this helps.
In my react/redux project, I call a function from my action to fetch a data from an api. Fetch starts the api request... but react doesn't recognize dispatch()
function getAuthenticatedUser() {
....
return fetch("my.api/path", requestHeaders)
.then(response => handleResponse(response))
.then(response=>{
return response.json()
}).then(responseJson =>{
dispatch(requestSuccess(responseJson.user))
})
....
function requestSuccess(....
....
Then, I wrapped around return dispatch as follows. Now it outputs no error, but fetch() doesn't start any api requests. (No requests in Network/XHR)
return dispatch => {
return fetch("my.api/path", requestHeaders)
.then(response => handleResponse(response))
.then(response=>{
return response.json()
}).then(responseJson =>{
dispatch(requestSuccess(responseJson.user))
})
}
What am I missing?
I found the solution. Firstly I want to thanks to 3 comments on the question.
I firstly installed redux-thunk. I added a middleware to my store:
import thunk from "redux-thunk";
...
export const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
Than, I imported store in my component and dispatched my action function. (Previously I was calling it directly)
constructor(props) {
....
store.dispatch(userActions.getAuthenticatedUser())
Now fetch and dispatchers within fetch work fine.
I'm using typescript-fsa and typescript-fsa-reducers packages to simply create actions and reducers in TypeScript React application.
const actionCreator = actionCreatorFactory();
export function signInHandler(state: UserState, action: Action): UserState {
// ????
return { ...state };
}
export const signIn = actionCreator.async<SignInRequest, RequestResponse<SignInResponse>>("USER_SIGNIN");
export const UserReducer = reducerWithInitialState({ signedIn: false } as UserState)
.casesWithAction([signIn.started, signIn.done], signInHandler)
.build();
Usage in component:
export default connect<StateProps, DispatchProps>(
(state: RootState) => ({} as StateProps),
(dispatch: Dispatch<RootState>) => {
return {
signIn: (userName: string, password: string) => dispatch(signIn.started(new SignInRequest(userName, password)))
};
}
)(SignIn);
And now I'm stuck. I don't know how to make HTTP calls to my API so I can send request when component dispatches action on dispatch next action when response from API arrives. I would like to use promises.
How to solve that?
In React without the typescript-fsa abstraction, you'd make async API callsat the action creator level, since actions are just dispatched POJOs and reducers are supposed to not have any side effects.
There are two projects that make it easy to do this, redux-thunk and redux-saga. I prefer redux-thunk because it is easier to wrap your head around. Basically your action creators get passed the dispatch function, and then they can be responsible for dispatching more than one thing... like so:
function asyncActionCreator(dispatch) {
dispatch(startAsyncAction());
doSomethingAsync()
.then(result => dispatch(completeAsyncAction(result))
.catch(err => dispatch(errorAsyncAction(err));
}
In your typescript-fsa world, there are some companion packages for both of these: typescript-fsa-redux-thunk and typescript-fsa-redux-saga.
It appears that typescript-fsa-redux-thunk takes a similar approach to the above example, using the concept of an "action worker", which coordinates the dispatching of actions via typescript-fsa. There is a really good example of doing this on the typescript-fsa-redux-thunk repo.