ReactJS / Redux call to dispatch action in other reducer - reactjs

How to dispatch action in other reducer.
For example i create reducer LogsReducer with switch case ADD_LOG and REMOVE_LOG. Code:
const addLog = (text) => ({type: 'ADD_LOG', text});
const removeLog = (id) => ({type: 'REMOVE_LOG', id});
const logsReducer = (state = {}, action) => {
switch (action.type) {
case 'ADD_LOG':
// todo
case 'REMOVE_LOG':
// todo
default:
return state;
}
};
And i add logsReducer to combineReducers.
To dispatch action i call this inside mapDispatchToProps.
dispatch(addLog('sample log'));
dispatch(removeLog(2);
And here question. How to allow to dispatch message from outside of logs reducer. For example from fetch response in other reducer eg. contactReducer?

How to allow to dispatch message from outside of logs reducer. For example from fetch response in other reducer eg. contactReducer?
Usually presence of asynchronous API calls or action dispatching queue meant that presentation/domain logic is consists of process, so process-manager is needed for resolve that task.
General solution is redux-saga, which combines middleware and live process manager. With using saga, components just notify about logical happened action, and then saga performs API calls, produce new actions and even do Optimistic updates.
Also, with redux+saga approach your web-application automatically become full-stack Event-Sourced application: by writing front-end code you will get also isomorphic back-end code (If use some tool like that https://github.com/reimagined/resolve )
See practices with using saga Infinite loop with redux-saga and how to setstate after saga async request

Related

useEffect hook of react vs redux thunk middleware in redux

use effect hook is used to perform the side effects like network requests in react.
redux-thunk middleware is also used to perform the side effects like network requests in react.
I'm pretty confused, is there any difference in their real application, or is it just a matter of choice.
The purpose of thunk is not to perform side effects by definition.
In Redux world, the actions must be plain objects with a required key type. An example:
const increaseAction = { type: "INCREASE" };
If you want to create a function that returns an action, this function should also return an action object but nothing else. Otherwise you cannot dispatch the action creator function itself.
// Create an action creator
const increase = () => {
return { type: "INCREASE" };
}
// Now you can dispatch the result of this function
dispatch(increase());
However, when dealing with asynchronous network requests, you probably want to dispatch multiple actions that updates your state accordingly based on the current state of your network request.
// When starting network request
dispatch({ type: "FETCH_START" })
// When network request is successful
dispatch({ type: "FETCH_SUCCESS" })
// When network request fails
dispatch({ type: "FETCH_ERROR" })
That's why action creator functions that deals with network requests or asynchronous operations return another function that takes dispatch as its parameter. This return function is handled by thunk middleware. Now we can use the dispatch function from the parameter to dispatch our actions.
const fetchData = () => async (dispatch) => {
dispatch({ type: "FETCH_START" });
try {
const data = await fetch("http://somedata.com/something").then(res => res.json());
dispatch({ type: "FETCH_SUCCESS", payload: data });
} catch {
dispatch({ type: "FETCH_ERROR" });
}
}
If you realized, we did not return anything inside fetchData. Instead, we used the dispatch parameter from the function that is returned by fetchData. When you dispatch(fetchData()), thunk transforms your action creator functions into plain objects; wait for the network requests to be resolved or rejected, then dispatch the appropriate action based on the result of your network request.
Now where does useEffect fall into this equation?
useEffect is the React hook that mimics the React lifecycle methods from class components. If you want to make a network request, or any asynchronous operation, you can do it inside useEffect. Following the Redux example above, you would call dispatch(fetchData()) inside useEffect.
Redux thunk is if you are using redux and are doing something asynchronously. E.g such as writing to a database.
if you are just using functional components in React and you want to update the ui then you would use useEffect to check for the change. If you are using class based components then there is a built in method componentDidMount. Built in as in you don't have to import it in along with React. Which you need to do for useEffect.
Here is the page for hooks, that talks about how it is used.
https://reactjs.org/docs/hooks-effect.html
Here is the page for thunks
https://redux.js.org/usage/writing-logic-thunks

How to set the data in Redux when I am fetching something?

Good evening,
I am making weather app with react and redux. I have my reducer with a object of my data info with empty string etc:
const initState = {
city: '',
temp: '',
wind: ''
}
And for example in React I am gonna fetch all the data and what should i do now?
I should have dispatch action (FILL DATA - for example) and then i should make a [useState] inside my component for temporary place for my data. Then fill the data in my temporary place and then useDispatch to update redux store?
Actually, there are a few ways to do this, such as:
Use a redux middleware like redux-saga or redux-thunk, which I recommend.
In the request action, make the asynchronous call and then dispatch the success action to update the state data.
Call the API from the component and call the state redux store update action to update the global state. NOT RECOMMENDED.
Apparently, you're trying to use the third way, but it's not recommended because it beats the purpose of abstract redux and making the data scattered all around. A bad practice.
A middleware example would be very long, so I'll try to explain the second way briefly.
In your request action, do something like this:
...
axios.get(url).then(res => {
dispatch({ type: 'REQUEST_SUCCESS', data: res.data });
});
In your reducer:
...
switch (action.type) {
case: 'REQUEST_SUCCESS';
return { ...state, ...action.data };
}
There is a library called redux-thunk that will help you with this. It allows actions to be asynchronous, so you can actually dispatch the action, fetch all the data INSIDE the action and then dispatch an action to fill your state. After configuring it, your action would look something like this:
const getWeatherData = () => { // Declare it like any other action
return async (dispatch) => { //Then return a promise
const response = await getWeatherAPI(); // fetch from the backend
return dispatch({ // And then dispatch another action to populate your reducer
type: "FILL_DATA",
payload: response
})
}
}
This action would be dispatched from your code just like any other action, but considering it returns a promise you can actually await for it to finish and handle that any way you want.

Add Loading Indicator for React-Redux app with Promise Middleware

I am new to react redux. I want to add a loading text when the user pressed the search button and dismiss the text when data comes back or the action completed.
In a normal async function, I can just toggle the isLoading flag before and after the call back.
However, in my app, I am dispatching an action that returns a 'type' and 'payload' that is a promise. The middleware redux-promise then 'automatically' converts that promise payload and send it to the reducer.
In other words, the middleware does the .then action for the promise behind the scene and gives my reducer the correct data.
In this case, I am not sure how I can add a loading text to my view because as soon as I call this.props.getIdByName(this.state.value), I do not know when the data comes back.
The logical place for me would be inside the reducer since that is when the data comes back. However, I belive that would be a bad way because reducers should not perform such task?
Inside my component, I have the following function for my submit
handleSubmit(e) {
e.preventDefault();
this.props.getIdByName(this.state.value);
}
Inside my actions/index.js file, I have the following action generator
export function getIdByName(name) {
const FIELD = '/characters'
let encodedName = encodeURIComponent(name.trim());
let searchUrl = ROOT_URL + FIELD + '?ts=' + TS + '&apikey=' + PUBLIC_KEY + '&hash=' + HASH + '&name=' + encodedName;
const request = axios.get(searchUrl)
return {
type: GET_ID_BY_NAME,
payload: request
}
}
Inside my reducers/reducers.jsx
export default function (state = INITIAL_STATE, action) {
switch (action.type) {
case GET_ID_BY_NAME:
console.log(action.payload.data.data.results[0].id); <-- here the data comes back correctly because reduer called the promise and gave back the data for me
return {...state, id: action.payload.data.data.results[0].id};
default:
return state;
}
}
And in my main index.js file, I have the store created with the following middleware
const createStoreWithMiddleware = applyMiddleware(
promise,
thunk
)(createStore);
When you dispatch an action with a Promise as its payload while using redux-promise, the redux-promise middleware will catch the action, wait until the Promise resolves or rejects, and then redispatch the action, now with the result of the Promise as its payload. This means that you still get to handle your action, and also that as soon as you handle it you know it's done. So you're right in that the reducer is the right place to handle this.
However, you are also right in that reducers should not have side-effects. They should only build new state. The answer, then, is to make it possible to show and hide the loading indicator by changing your app's state in Redux :)
We can add a flag called isLoading to our Redux store:
const reducers = {
isLoading(state = false, action) {
switch (action.type) {
case 'START_LOADING':
return true;
case 'GET_ID_BY_NAME':
return false;
default:
return state;
}
},
// ... add the reducers for the rest of your state here
}
export default combineReducers(reducers);
Whenever you are going to call getIdByName, dispatch an action with type 'START_LOADING' before calling it. When getIdByName gets redispatched by redux-promise, you'll know the loading is done and the isLoading flag will be set back to false.
Now we have a flag that indicates whether we are loading data in our Redux store. Using that flag, you can conditionally render your loading indicator. :)
I am sure Pedro's answer should get you started but I recently did the very exact loader/pre-loader in my app.
The simplest way to do it is.
Create a new key in the state object in one of your reducers and call it showLoader: false.
In the same container as before(the one with the button), create a mapStateToProps and get that state property showLoader.
Inside the container that holds the button you are trying to trigger the loader with, add an onClick event which calls an action, say displayLoader. In the same function also set the this.props.showLoader to true
Create a displayLoader action write something like this:
function displayLoader() {
return {
type: DISPLAY_LOADER,
payload: {}
}
}
In the reducers, catch this action and set the showLoader back to false when your desired payload is received.
Hope it helps!

Redux thunk actions and sharing state

I would like my application to be aware of when async actions are fired and when they complete. The reason for this is that I would like an overall loading state for my applications which sets to true when all async actions have completed. An early implementation of this used a global counter for callbacks.
These actions could be in different files and be listened to by separate reducers, responsible for different branches of state.
I'm using thunk-middleware combined with redux-promise-middleware so one of my async actions might look like this:
export const requestSiteData = () => (dispatch, getState, api) => {
const url = '/endpoint';
return dispatch({
type: 'STATE_BRANCH/REQUEST_SOME_DATA',
payload: api.fetch(url)
}).catch(() => {});
};
This would then dispatch STATE_BRANCH/REQUEST_SOME_DATA_FULFILLED or STATE_BRANCH/REQUEST_SOME_DATA_REJECTED when complete.
Because of the promise middleware I'm finding it hard to intercept the call and count/determine how many ajax requests have been made and how many have completed because the actions are pre-determined and I can't dispatch another action from my reducers.
I can update state there obviously but the async actions may be split across several parts of the state tree so I don't have an overall place to manage the requests.
Does anyone have any ideas how I might go about solving this type of problem. Shout if the description/use case isn't clear.
There is a Redux middleware based around redux-promise that will help you keep track of pending actions:
redux-pending
A thunk is a chain of function that executes one by one so in your case your code for async call should be
export function asyncAction() {
return function(dispatch){
fetchCall().then((data)=>data.json())
.then((data)=>disptach(handleAsyncDataAction(data))
}
and fetchCall() function should be write as.
function fetchCall(){
return fetch('your_url');
}
and your handleAsyncData(data) action will return the result to the reducers so at the end for one fetch call you have to write 3 methods.
1. function you will call from Component
export function asyncAction(){..}
2. function who will written promise to this function
function fetchCall(){...}
3. and last function which will return handle return data and send it to reducer.
function handleAsyncData(data){....}

chaining multiple async dispatch in Redux

I am trying to chain multiple actions together in the following fashion:
A. post user data to database
B. use posted data to query Elasticsearch for results
(I do A and B in parallel)
B1. with results from ES, query original database for results from two tables
B2. navigate to new page and update UI
I am using thunks right now to reason about my code, but I also found this async pattern to be extremely verbose:
export function fetchRecipes(request) {
return function(dispatch) {
dispatch(requestRecipes(request))
return fetch(url)
.then(response => response.json())
.then(json => dispatch(receiveRecipes(request, json))
)
}
}
this, along with "requestRecipes" and "receiveRecipes" as other action creators seems like quite a bit just to make one async call. (a request, a receive, and a fetch function)
summary: when you're chaining 2-3 async actions whose outputs depend on each other (I need to promisify when possible), is there a more efficient means of doing so without writing 3 functions for each async call?
I figure there had to be a way. I'm pattern matching off of the Redux docs and soon became very overwhelmed with the functions I was creating
thanks a lot for the feedback!
You can use redux-saga instead of redux-thunk to achieve this more easily. redux-saga lets you describe your work using generators and is easier to reason about.
The first step is to describe how you pass your data to redux without worrying about services or async stuff.
Actions
// actions.js
function createRequestTypes(base) {
return {
REQUEST: base + "_REQUEST",
SUCCESS: base + "_SUCCESS",
FAILURE: base + "_FAILURE",
}
}
// Create lifecycle types on `RECIPES`
export const RECIPES = createRequestTypes("RECIPES")
// Create related actions
export const recipes = {
// Notify the intent to fetch recipes
request: request => ({type: RECIPES.REQUEST, request})
// Send the response
success: response => ({type: RECIPES.SUCCESS, response})
// Send the error
error: error => ({type: RECIPES.FAILURE, error})
}
Reducer
// reducer.js
import * as actions from "./actions"
// This reducer handles all recipes
export default (state = [], action) => {
switch (action.type) {
case actions.RECIPES.SUCCESS:
// Replace current state
return [...action.response]
case actions.RECIPES.FAILURE:
// Clear state on error
return []
default:
return state
}
}
Services
We also need the recipes API. When using redux-saga the simplest way to declare a service is to creating a (pure) function which reads the request as argument and returns a Promise.
// api.js
const url = "https://YOUR_ENPOINT";
export function fetchRecipes(request) {
return fetch(url).then(response => response.json())
}
Now we need to wire actions and services. This is where redux-saga come in play.
// saga.js
import {call, fork, put, take} from "redux-saga/effects"
import * as actions from "./actions"
import * as api from "./api"
function* watchFetchRecipes() {
while (true) {
// Wait for `RECIPES.REQUEST` actions and extract the `request` payload
const {request} = yield take(actions.RECIPES.REQUEST)
try {
// Fetch the recipes
const recipes = yield call(api.fetchRecipes(request))
// Send a new action to notify the UI
yield put(actions.fetchRecipes.success(recipes))
} catch (e) {
// Notify the UI that something went wrong
yield put(actions.fetchRecipes.error(e))
}
}
}
function* rootSaga() {
yield [
fork(watchFetchRecipes)
]
}
And that's it! Whenever a component will send a RECIPES.REQUEST action, the saga will hook up and handle the async workflow.
dispatch(recipes.request(req))
What's awesome with redux-saga is that you can easily chain async effects and dispatch actions during the workflow.
Based on your description, the only time you actually update your UI is right at the end of all these asynchronous operations (B1).
If you don't use the results from the preceding async calls to change your application state / update your UI, what is the benefit of having these fine-grained actions?
Of course there are things like "loading / request started" and "finished loading / request stopped", but it seems to me, that in your case, you could just do the chained async calls outside of redux (in some kind of API-layer) and only use one action.
This action dispatches a "REQUEST_STARTED", then calls the API-layer, which does the DB-calls and elasticsearch request etc., and then dispatches either "REQUEST_SUCCESS" or "REQUEST_FAILURE", based on the result of the promise, which will give you the data you need to update your UI.
This way, the state in redux only concerns itself with ONE side-effect, instead of the implementation details of your chained calls. Also, your action gets a lot simpler, because it just handles the results of one async call.

Resources