In Redux, is there the concept of Data Provider? - reactjs

Basically I want to implement an architecture of the type
UserManager.getUser(22)
getUser() -> ReduxStore -> (Does not contain an user with ID 22) -> Goes to User Provider -> User Provider goes to API and returns User object.
Redux Store then saves for subsequent requests and returns User object.

Redux has unidirectional data flow, so the writing and the reading of data are decoupled.
Components read Redux data by subscribing to the store via connect or useSelector, and they write data via disptaching actions in the store.
A selector takes in state and returns a subset of the state, but it does not change the state. A dispatched action can change the state, but it does not return any state.
CQRS (Command Query Responsibility Segregation) is one of the motivations behind Redux. The idea of CQRS is basically to:
use a different model to update information than the model you use to read information
In Redux the update-model is the actions and the read-model is the selectors. To combine them into a single "provider" would be to defeat the purpose of Redux's design.
But if you absolutely had to conflate the two concerns, it might be possible to somehow combine a selector and action-dispatch with a thunk. Again, though, it would not be idiomatic Redux.

Yes we call them action creators. Let's say you're using redux thunk for side effects so getUser will be an action creator that'll first query redux store to see if there is data available if not it'll fetch it from server and store in the redux store like this:
function getUser(id) {
// getState is a function that gives us access to entire redux store
return (dispatch, getState) => {
try {
let user = getState().users.find(x => x.id === id) || null;
if (user) {
dispatch({ type: 'GET_USER', payload: user })
return;
}
user = fetchUserFromServer(id);
dispatch({ type: 'GET_USER', payload: user })
} catch(error) {
// handle error here
}
}
}
Now when next time getUser is called there will be data for that user in the redux store and a call to server will be avoided.
Hope it helps :)

Related

React Redux Multiple Actions, want to trigger a single render

New to react/redux so bear with me :).
When selecting a regionlevel from a dropdown, I want to store the selected RegionLevelId, and make an API call to fetch the regions that belong to this RegionLevel.
At first I was storing this regionLevelId in the regionLevelReducer, which made it so that I only triggered one state change.
However, I'm saving more selected options in my state so I figured it might be cleaner to make a "selectOption" reducer that stores the selectedIds.
The point is now however, that when I select a regionLevel from the dropdown, I have to make two action calls. One to store the regionLevelId, and one to fetch data from the API. This renders the page twice, which is unnecessary.
I was wondering if it's possible to call two actions, while rendering AFTER these two actions have been completed. Now it renders in between these two actions.
I'm not sure if this is defying of how redux should be used. But I'm interested in how people would solve this problem.
The action I'm using atm:
export function selectRegionLevel(regionLevelId) {
return function(dispatch) {
dispatch({
type: "SELECT_REGION_LEVEL",
payload: regionLevelId
});
dispatch({
type: "FETCH_REGIONS",
payload: {
request: {
url: `regionLevels/${regionLevelId}/regions`
}
}
});
};
}
Thanks.
From your requirement, i think what you are looking for is Batched Action. Take a look at react-batched-actions which aims to solve this problem and is recommended by redux team in such scenerios.

Where is the best place to store an RTCPeerConnection object in a React/Redux app?

RTCPeerConnection is an object with certain methods that when called mutate the object (for example, setLocalDescription, addIceCandidate). These methods get called based on received signaling from the other side of a WebRTC connection (like when you receive an offer, or an ice candidate).
Therefore, this object does not seem well-suited for being in a redux store, since the developer doesn't at a first approximation have control over the mutations, and in a redux reducer you can't just create a copy of an RTCPeerConnection as this would eliminate your previous webRTC session.
However, in a WebRTC app that uses React, perhaps different components need access to the RTCPeerConnection object (for instance, maybe it is instantiated on mount of the top level component in the app, but then in some UI component like a modal deep in the tree that accepts a call, you want to call a method on RTCPeerConnection to create an answer to the webRTC offer that was received. Or maybe a deeply nested component needs to initiate a call). Is it the case that the only solution is to pass the object as props down the component tree? Is there no way to use redux with a complex object like this?
UPDATE: considering the answer below about using middleware for handling socket.io, let me reframe my original question: would it make sense, if I have an RTCPeerConnection object as state in a top-level component, to build middleware that that handles dispatch calls that ultimately must receive some way some how a reference to the original RTCPeerConnection to make a method call such as setRemoteDescription?
The standard place for "socket"-like connection objects (websockets, Firebase, etc) in a Redux app is in a middleware. Any part of the application that needs to tell the socket to do something can dispatch an action that is intercepted by the middleware, and the middleware can dispatch actions to update the state in response to received messages.
There's dozens of existing examples of middleware for various sockets in the Middleware - Sockets and Adapters section of my Redux addons catalog.
update
Here's a quick example of what an RTC middleware might look like. The code is completely untested, but this should illustrate the idea:
function createRtcMiddleware() {
return (store) => {
let rtcPeerConnection = null;
return (next) => action => {
switch(action.type) {
case "RTC_CONNECTION_CREATE": {
const {rtcConfig} = action;
rtcPeerConnection = new RTCPeerConnection(rtcConfig);
rtcPeerConnection.somecallback = () => {
// maybe dispatch a Redux action in response to
// a received message
};
// Do not pass the action down the pipeline, since only
// this middleware cares about it
return;
}
case "RTC_CONNECTION_SET_DESCRIPTION": {
if(rtcPeerConnection) {
rtcPeerConnection.setDescription(action.description);
}
return;
}
}
// If we don't care about it, pass it down the pipeline
return next(action);
}
}
}

State changes in Redux-Saga

I have a simple React App. It allows a user to edit a form to update data in a database. On Submit the page generates an action with the form data. A Redux Saga yields to the action and asynchronously updates the database. This all works.
However in one case the update is slightly more involved. Not only must new data be added but any data deleted on the form must be deleted from the database in a series of API calls. To do this I need to know what has changed (e.g. what has been deleted) rather than just the new state.
How can my saga have access to this information? I could calculate it in the reducer because that has access to the previous state but it is commonly held to be an anti-pattern for the reducer to then dispatch a new action.
Sagas have a select effect available, which just runs a selector function and returns the extracted state. You can use this to retrieve the old and new items from the Redux store, and deal with the changes from there:
function* handleFormUpdates() {
const oldItem = yield select(selectOldItem);
const newItem = yield select(selectNewItem);
const changes = diffTheItems(oldItem, newItem);
// make API calls to deal with changes appropriately
}
Overall, this is a good reason to keep the "temporary" or "draft" state in Redux, so that you can make use of both the "current" and "draft" values in your logic.
I discussed some related concepts in my blog posts Practical Redux, Part 7: Form Change Handling, Data Editing, and Feature Reducers and Practical Redux, Part 8: Form Draft Data Management
...any data deleted on the form must
be deleted from the database in a series of API calls. To do this I
need to know what has changed (e.g. what has been deleted) rather than
just the new state.
If I understand correctly you have form state saved in a redux store and you need to know when and what has changed. You could create your own watcher saga:
function* watchFormUpdates(){
while (true) {
const oldFormState = yield select(selectors.selectFormState);
const action = yield take (actionTypes.FORM_UPDATE); // wait until action is dispatched
const newFormState = yield select(selectors.selectFormState); // at this point store has been updated
// compare oldFormState with newFormState...
if(oldFormState.hasSubscription && !newFormState.hasSubscription) {
yield fork(deleteSubscriptionSaga); // start non-blocking worker task
// or just dispatch action - yield put(actionCreators.deleteSubscription());
}
}
}

Sending error message from reducer to user

I'm new to React and Redux and I'm trying to write a simple application where a person can submit a URL for an image and it will show up on the page. Note that there is no backend to the application as of yet.
export const addImage = (url) => {
return {
type: ADD_IMAGE,
key: Guid.create().toString(),
payload: url
}
}
Adding an image creates an action of type ADD_IMAGE and my reducer updates the state consequently. However I also check if the URL is already in the list.
switch (action.type) {
case ADD_IMAGE:
if (state.find(image => image.url === action.payload)) {
return state;
} else {
return(
[
...state,
{key: action.key, url: action.payload}
]
);
}
break;
default:
}
The problem is that when I deny a post because the URL is already in the state I also want to convey that message to the user by showing it in a div next to the form. From what I've read I think I'm not supposed to try to access React state from reducers (if that is even possible) and... well.. I'm just stuck. I've been trying to find a simple guide on how to do this but I find nothing I can quite understand. After adding a database I guess I will have to do this as part of the async process but as I have it now I guess there should be some kind of simple solution.
You are starting to introduce logic into your reducer and this will inevitably lead to situation where you need to process some state outside of the reducer's scope.
The solution is to transfer your reducer logic into a thunk using a middleware package such redux-thunk (or similar package). This allows you to treat special kinds of actions as functions which means you can extend a plain action with specific action-related logic. The example you give of needing to dispatch an error action under certain conditions is an excellent use-case for redux-thunk.
Below is a example of how you might pull the logic out of your reducer into a thunk. You should note that, unlike reducers, thunks explicitly support fetching state and dispatching subsequent actions via the getState and dispatch functions.
Thunk example
export const addImage = (url) => {
return (dispatch, getState) => {
const key = Guid.create().toString()
dispatch({
type: ADD_IMAGE,
key,
payload: url
})
const state = getState()
// you would want to use a `selector` here to locate the existing image
// within the state tree
const exists = selectors.images.exists(state, url)
if (exists) {
dispatch(actions.ERROR_IMAGE_EXISTS({key, url}))
}
}
}
A note on selectors
You will see that I am using a selector to determine if the image exists. In the same way that thunks are the place to put your dispatch logic, a selector is the place to put your state-traversal logic. They are used to return portions of the state-tree or provide simple state-utilities such as the exists function shown above. Packages are available to help, for example reselect.
Follow on questions from comments
Are selectors not a built-in thing in Redux?
No they are not. Selectors are an idea that builds on top of redux and the concept exists as a place to put your state searching, caching, reading logic. This extracts the sometimes complex state traversal logic out of your thunks and components and into a nice tidy, structured collection of selectors.
Why use a selector instead of state.images.find(i => i.url === url)?
If you use a selector package then you get far more benefit than just a good separation of concerns, you get a big performance improvement (see usage example below).
Here are the headlines from the popular reselect package:
Selectors can compute derived data, allowing Redux to store the minimal possible state.
Selectors are efficient. A selector is not recomputed unless one of its arguments change.
Selectors are composable. They can be used as input to other selectors.
Why doesn't actions.ERROR_IMAGE_EXISTS(url) work for me
Because I just made that up for the example. The point is that you can dispatch actions from within the thunk, how you declare or get access to the action is up to you. I tend to centralise all my shared actions into an actions object that I import.
Selector usage example
Here is an example from my real-life code that shows how I use selectors to passing portions of the state as props to a react component:
const mapStateToProps = (state) => ({
model: services.model.selector.getSelected(state),
build: services.build.selector.getLastBuild(state),
recommendations: services.recommend.selector.getRecommendations(state)
})
Each of these selectors is finding the correct portion of the state tree and delivering it back ready for use. Nice and tidy, and if you use reselector, very efficient.

What are the most important considerations when designing Redux actions?

If I have a situation: a component has 3 operations(list all info || search needed info || filter info), but all of these operations depend on same API, just different parameters.
I'd like to know what is the best approach to design Redux actions? To design 3 actions that are mapping the 3 operations? Or just 1 action because of just 1 API?
What are the most important considerations when designing Redux Actions?
Whether it should be one or two or three actions really depends on how your reducers are organized, and on how your UI is connected to your store.
You should think of API calls as side-effects of your actions, and not as the actions themselves. Your redux store holds a particular state of your UI: what filter is active ? What is the current search query ? and the like...
The most important consideration when designing actions is : what do they mean for the state of my UI? What impact do they have on it ? How do they relate to my store's state ?
If your concern is to mutualize API calls code, there are various patterns to handle that : use a simple helper function taking parameters, use something like redux-saga to trigger side-effects, etc.. But all in all, API calls shouldn't be relevant when designing actions.
There's nothing in the docs that says that actions should map to API endpoints. So you shouldn't have to think of how many API endpoints you have.
From http://redux.js.org/docs/basics/Actions.html:
Actions are payloads of information that send data from your
application to your store.
In your case though, if one action with payload can accomplish the result and mutate your state to the desired condition, I don't see a reason to use three (and thus have three reducers, etc).
The redux docs have a good section on how asynchronous actions/API usage should work within a redux app.
http://redux.js.org/docs/advanced/AsyncActions.html
I've followed this pattern, using the thunk middleware, and found it to work really well. The idea is to give your actions the ability to return a function. You will have:
A "main" function that will dispatch that the application is making an API call, make the API call, and handle the results.
Two pure action creators (functions that return an object with a type and any number of other properties) that will pass the information related to the API call to your reducers.
The actions:
/* Inside ./action.js */
/* This is the function you will dispatch from another component (say, a container component. This takes advantage of thunk middleware. */
export function fetchAllInfo() {
return function(dispatch) {
dispatch(requestAllInfo())
/* Import your API function, make sure it takes a callback as an argument */
API.fetchAllInfo((allinfo) => {
dispatch(receiveAllInfo(allinfo))
}
}
/* Pure action creators */
function requestAllInfo() {
return {
type: "REQUEST_ALL_INFO",
}
}
function receiveAllInfo(allinfo) {
return {
type: "RECEIVE_ALL_INFO",
allinfo,
}
}
The reducer:
/* Inside ./reducer.js */
export function inforeducer(state, action) {
switch(action.type) {
case "REQUEST_ALL_INFO": {
return Object.assign({}, state, {
/* This can be passed as prop so a component knows to show a loader */
isFetching: true,
})
case "RECEIVE_ALL_INFO": {
return Object.assign({}, state, {
isFetching: false,
allInfo: action.allInfo,
})
}
default:
return state
}
}
So with thunk middleware (or your own way of dealing with asynchronous dispatching) and a callback on each API call, you can use this method to work with API calls dispatching at both the beginning of the call and when you receive results.

Resources