I'm learning redux (in Flutter using Firestore, but I don't think that matters), trying to get beyond the basics, and I'm confused about where 'computed state' (not even sure what to call it) should go.
Say I have app state like this:
user: an instance of user
movies: a list of user's movies
recentFavoriteMovie: one of the movies above that user has marked "favorite" and has the most recent creation date
I'm able to set user (login success action) and request user's movies (login success middleware). When the movies query completes, I'm confused about where to initialize recentFavoriteMovie. There seems to be many choices....
SetMovies middleware can compute it, and then call a SetRecentFavorite action.
Can the SetMovies reducer do it? Or is that considered a side-effect of the reducer, which is not allowed?
I'd like to do it lazily. Is it considered okay in redux to give the app state object a method that computes and caches it? If so, I'd still need to clear the cached value when a new movie list is set. But that seems like the same problem as (b) above.
I could put the movies property and favoriteMovies (property or method) in my user object (they kinda belong there), and then just dispatch an UpdateUser action each time one or the other changes. In general, I don't know when/whether to "promote" some sub attribute of my app state to the top level so the app can react to it.
Are all or any of these valid choices? I hope this makes sense as a question. I might even be too behind the curve to ask this properly.
You almost ther with computed state. From documentation
Reselect provides a function createSelector for creating memoized selectors. createSelector takes an array of input-selectors and a transform function as its arguments. If the Redux state tree is changed in a way that causes the value of an input-selector to change, the selector will call its transform function with the values of the input-selectors as arguments and return the result. If the values of the input-selectors are the same as the previous call to the selector, it will return the previously computed value instead of calling the transform function.
That is essentially what yu want with lazy selection of movies.
In state you store user and movies. Some movies marked as favorites for specific user (so when user marks movie as favorite you only modify movies and not re-run selector).
When some component needs list of favorite movies, it calls selector which computes derived state (list of favorite movies) and returns it. Also selector will memorize results and recompute them only when store changes but not on each render.
This approach can be considered best practice as you may later implement some filters for movies list and selectors will help to extract filtered list of movies.
When using selector you don't required to store selected data (list of favorite movies) in you store.
Computed state is used in mapStateToPros for each component that require computed state like so
const makeMapStateToProps = () => {
const getFavoriteMovies = makeGetFavoriteMovies();
const mapStateToProps = (state) => (
{
favoriteMovies: getFavoriteMovies(state.user, state.movies),
movies: state.movies,
user: state.user
});
return mapStateToProps;
}
And makeGetFavoriteMovies may look like
const getFavoriteMovies = (state) => state.movies;
const getUser = (state) => state.user;
export function makeGetFavoriteMovies () {
return createSelector(
[getFavoriteMovies, getUser],
(favoriteMovies, user) => {
// Here can be complicated logic to select only favorite movies
return movies.filter (movie => movie.isFavorite);
}
);
)
Reducer and/or middleware can compute favorite movies list. But this is not their responsibility. So to better separate concerns its better to use selectors for this task.
Also there is not reason for specific middleware (ogin success middleware). You may implement logic in actions and reducer.
Related
Hey i'm new to RTK toolkit and just trying to access state from isolated component to store state from mutation, so i can skip passing them to that component.
I can see the result from mutation in redux store behind the cashe key, but how to access the store data from isolated component?
I read the documentation at least twice and saw that if it's QUERY i can pass useGet** hook with the same args and could get the result. But my case is to access result from mutation (POST query).
Just in case i can create selector to the whole state:
export const selectSubmitStatus = (state) => state[createApi.reducerPath];
and possibly manual filter current item, but it could be easier to access mutation submitState with selector and passing cashe key or?
Simmilar problem here described
When using a selector, I thought that I could do whatever I wanted with the variable without modifying the state, so I was surprised that the state became mutated.
So if this is wrong (in a redux saga):
const filters = yield select(state => state.filters.filters);
filters.terms['helloo'] = "mutated";
//send data with request
yield put(requestData(filters)
How come that first line is a direct reference to the state?
Anyway, if I try using Object.assign, it also mutates state:
const filters = Object.assign({}, yield select(state => state.filters.filters));
filters.terms['helloo'] = "mutated";
How do I create a selection that is a copy of the state?
There's truly no "magic" involved here. Redux's getState() is literally just return state, and both hand-written selectors and Reselect return whatever you have written the functions to return. So, in that example, filters is the actual object reference that's nested inside the store state, because that's what your function returned.
Per the Redux docs page on "Immutable Update Patterns", you need to copy all levels of nesting that you want to update. In your example, you're making a copy of filters, but not filters.terms, so terms is also still the original object that's in the store. You would need to make a copy of that as well, and modify the copy.
I am creating simple app, which makes GET requests to the server, then prepares recieved data and creates chart. There are few questions:
Where should I place code responsible for checking and preparing raw data. Currently I have it in my action creators, but maybe it needs to be in the component itself?
I need to check and compare prepared data with the data which is already used for the chart, and do not call re-render if it's the same or not valid. Where should I put this check? For now I think to place it inside action creators too. But for that I need to use getState() for accessing the state, doesn't look right.
Action creators seems right place for all these checks for me, because if data is not valid, I can simply not update my state with it, (e.g. do not dispatch certain action creator) Or maybe I have to update state with new data despite it is not valid?
given these action creators, what is the best place for described checks?:
export function fetchPopulations(term = "") {
return function (dispatch) {
dispatch(fetchingPopulations())
term=toTitleCase(term)
return fetch(`${API_URL}${term.replace(/\s/g, '%20')}`)
.then(response => response.json())
.then(json => dispatch(requestPopulations(json)))
}
}
export function requestPopulations(data = []) {
return {
type: REQUEST_POPULATIONS,
payload: data,
}
}
export function fetchingPopulations() {
return {
type: FETCHING_POPULATIONS
}
}
I would say you are doing it right.
In your example, requestPopulations and fetchingPopulations are the real action creators and fetchPopulations is a composing function (yes, composing functions for the win!).
Where should I place code responsible for checking and preparing raw
data. Currently I have it in my action creators, but maybe it needs
to be in the component itself?
Components are not the place for putting the business logic of our application. Components should only represent the View in our MVC. No API calls, no business logic, only props and state.
I need to check and compare prepared data with the data which is
already used for the chart, and do not call re-render if it's the same
or not valid. Where should I put this check? For now I think to place
it inside action creators too. But for that I need to use getState()
for accessing the state, doesn't look right.
Create modular functions (it really shines with code maintenance and reuse) for performing these checks, compose them together in another one along with your real action creators, and you can dispatch only if needed. Further optimization can be done inside component life cycle hook shouldComponentUpdate(nextProps, nextState). Also I think it is definitely not an anti-pattern to use methods with a signature like this:
export function myComposingFunction(params) {
return (dispatch, getState) => {
// ...
So you can use getState().
Action creators seems right place for all these checks for me, because
if data is not valid, I can simply not update my state with it, (e.g.
do not dispatch certain action creator) Or maybe I have to update
state with new data despite it is not valid?
No, do not update the state with useless data. If you do that you will re-render the entire tree for nothing. You were absolutely right to say "if data is not valid, I can simply not update my state with it, (e.g. do not dispatch certain action creator)"
I would like many different redux actions in my app to all trigger common functionality in a specific reducer. I would like to avoid having to either repeat some flag in every action creator (like doThing: true) that the reducer looks for. I also don't want to have to have the reducer just look for every individual action that falls into this category, since that also requires someone to remember to do this every time they add a new action, like adding the flag.
I was thinking of dispatching a second action every time one of these actions is going to be dispatched. This would not be hard to do, but I'd rather not have 2 actions dispatched every time one thing happens. It seems like it would pollute the state history.
Is there a common way of solving this problem?
For more context to my specific problem, the specific feature is related to the API client my app uses to talk to our API. On every successful response, we'd like to do something in a reducer to update the state, and on every failed response, we'd like to do something else.
There are many different success and failure actions (such as ITEM_FETCH_SUCCESS or WIDGET_UPDATE_FAILURE), and adding a flag to all of them would be hard to remember to do when new ones are added.
Since all api requests go through a single function, that function COULD dispatch generic REQUEST_SUCCESS and REQUEST_FAILURE actions. But this would mean every response from the server would dispatch 2 actions (REQUEST_SUCCESS and ITEM_FETCH_SUCCESS). This is obviously not ideal since it would mean many more actions in my state history.
Assuming the generic REQUEST_SUCCESS and REQUEST_FAILURE actions are updating their own specific portions of the state-tree then it is fine to dispatch them as distinct actions. Doing this does not necessarily imply the pollution of your state history but can simply be a better description of the app's intentions.
ITEM_FETCH_SUCCESS: Change state for item
REQUEST_SUCCESS: Change state for request
WIDGET_UPDATE_FAILURE: Change state for widget
REQUEST_FAILURE: Change state for request
You can see that whilst the actions are intimately related, they are not necessarily the same thing as they change different parts of the state tree.
Accepting this, the question is: How best to implement the action-pairs so that adding new actions does not mean remembering to add its corresponding REQUEST_* partner?
I would consider applying a simple redux middleware component. This could intercept the return from your api and dispatch the appropriate REQUEST_* action automatically.
Here is an example from some live code. This middleware intercepts a disconnect event raised by a websocket and automatically dispatches a custom action as a result. It at least shows the principle:
//Dispatch a disconnect action when the websocket disconnects
//This is the custom action provided by the middleware
import io from 'socket.io-client'
import { actions } from './action'
const websocket = ({ websocketUrl }) => store => {
const socket = io(websocketUrl)
socket.on('disconnect', () => store.dispatch(actions.disconnect()))
}
export default websocket
//Apply the custom middleware via the redux createStore function
//Also include the thunk middleware because it is useful
import { applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import websocket from './middleware'
function websocketize (opts) {
return createStore => (reducers, initial, enhancer) => {
const middleware = applyMiddleware(thunk, websocket(opts))
return createStore(reducers, initial, middleware)
}
}
export default websocketize
// Create the top-level redux store passing in the custom middleware enhancer
const opts = {websocketUrl: env.WEBSOCKET_URL}
const store = createStore(reducers, websocketize(opts))
This implementation keeps everything inside your reducers as opposed to having logic outside in an interception(middleware). Both ways are valid.
Try a sub-reducer pattern. I usually feel gross when I see it used(because it is usually used wrong), but your situation sounds perfect.
Extract duplicate functionality out of your reducers to one single
sub-reducer.
Then pass that reducer as a function to all others that need it.
Then pass the action and state onto the sub-reducer.
The sub-reducer does it's thing and returns that slice of state to
your parent reducer to allow you to do whatever you want with it
there (ie return it, mutate some more, some logic).
Also if you are tired of worrying about typing out "all the stuff" for async then I highly recommend you try out redux-crud.js
It also is possible and a simple way to do that would be to give every action to one reducer and let it do that common mutation, in a single case:
case actionOne
actionTwo
actionThree
actionFour: {
//do common stuff here
}
. But you said it is not duplicated, it is similar, which means your case becomes complicated by branching logic. I also don't recommend this. Keep cases simple so you can easily catch invalid mutations. This should be a super power of redux that it is easy to catch mutation errors. And for this reason and many others I don't recommend normalizing data in the front end.
Given a use case like the one in this question:
Best way to update related state fields with split reducers?
What is the best practice for dealing with actions in reducers that depend on state outside of their own state? The author of the question above ended up just passing the entire state tree as a third argument to every reducer. This seems heavy-handed and risky. The Redux FAQ lists the following potential solutions:
If a reducer needs to know data from another slice of state, the state tree shape may need to be reorganized so that a single reducer is handling more of the data.
You may need to write some custom functions for handling some of these actions. This may require replacing combineReducers with your own top-level reducer function.
You can also use a utility such as reduce-reducers to run combineReducers to handle most actions, but also run a more specialized reducer for specific actions that cross state slices.
Async action creators such as redux-thunk have access to the entire state through getState(). An action creator can retrieve additional data from the state and put it in an action, so that each reducer has enough information to update its own state slice.
In my use case, I have an action "continue" that determines what page a user is allowed to go to in a multiple-form / multi-step process, and since this depends on pretty much the entire app state, I can't handle it in any of my child reducers. For now, I've pulled the store into the action creator. I use the current state of the store to calculate an action object that fires to my "page" reducer, which changes the active page. I will probably install redux-thunk and use getState() in this action creator, but I'm not committed to this approach yet.
I guess this isn't too bad of a solution since there is only one action (so far) that must be handled this way. I'm just wondering if there is a better solution, or if there is a way to re-structure my state and reducers to make it easier, or if what I'm doing is within best practices for Redux. If there are any similar examples out there, that would be helpful also.
To give some more context, my state tree currently looks like this:
{
order: order.result,
items: order.entities.items,
activePage: {
id: 'fulfillment'
// page info
},
pagesById: { // all the possible pages
fulfillment: {
id: 'fulfillment'
// page info
}
}
}
The active page is the page / section in which the user must enter data in order to proceed to the next page). Determining the active page almost always depends on the items state and sometimes depends on order state. The end result is an app where the user fills out a few forms in succession, hitting continue once the form is valid. On continue the app determines the next page needed and displays it, and so on.
EDIT: We've tried the approach of implementing a "global" reducer in combination with child reducers.
The implementation is like this...
const global = (currentState = initialState, action) => {
switch (action.type) {
default:
return currentState
}
}
const subReducers = combineReducers({
order,
meta
})
export default function (currentState = initialState, action) {
var nextState = global(currentState, action)
return subReducers(nextState, action)
}
The global reducer is first run on the whole app state, then the result of that is fed to the child reducers. I like the fact that I'm no longer putting a bunch of logic in action creators just to read different parts of state.
I believe this is in alignment with the principles of redux since every action still hits every reducer, and the order in which reducers are called is always the same. Any thoughts on this implementation?
EDIT: We are now using router libraries to handle the page state, so activePage and pagesById are gone.
If state.activePage depends of state.order and state.items, you may subscribe to the store and in case of modifications on "order" or "items" then dispatch a "checkPage" action which can set another active page if necessary. One way should to connect on a "top component" order and items, listen their values and change active page/redirect
Not easy to understand your concern, I hope my message will help. Good luck