State Duplication in Redux

Let's say you have a book app using Redux with several features:

  • Buy books
  • Make a list of books.
  • Maintain a list of your own books.
  • Send recommendations to friends
  • Book Review

Now let's say that all these different modules and the common function in all these modules is the ability to search for books (for example, search for books to buy, books to add to your wish list, books to view, etc.).

These search results will be stored on several slices of the state tree.

An example state tree might look like this:

{ bookStore: { booksSearchResults: [], ..., }, wishlist: { booksSearchResults: [], ..., }, reviews: { newReview: { booksSearchResults: [], ..., }, ... }, ... } 

Are there any recommendations for managing such things? Would it just be to have a booksSearch state booksSearch and manage it through a common component?

What about cases where you may need to search for books in several places on the same screen (i.e. the navigation bar may have an autocomplete search function in addition to the search component in the main part of the application)?

Is there another approach to reusing search logic and somehow it updates different parts of the state (and if so, is there something to achieve this)?

+5
source share
4 answers

I have two tips for you:

  • Normalize your store.
  • Do not save search results in reduction.

Normalization

This is well documented, so I won’t go into it here. Suffice it to say that the normalization of your store will be flexible and help you justify your application. Think of your store as a server-side database, and it will be more reusable than if you adapted each section of the state to your views.

One way to do this for your application:

 { books: { bookId: { bookDetails, reviews: [ reviewId ], ownedBy: [ userId ], wishlistedBy: [ userId ], recommendations: [ recommendationId ] } }, users: { userId: { userDetails, reviews: [ reviewId ], wishlist: [ bookId ], ownedBooks: [ bookId ], friends: [ userId ], sentRecommendations: [ recommendationId ], receivedRecommendations: [ recommendationId ] } }, reviews: { reviewId: { bookId, userId, reviewDetails } }, recommendations: { recommendationId: { sender: userId, recipient: userId, bookId, recommendationDetails } } } 

You may not need all of these relationships, so don’t feel that you need to realize all this. But starting with a database like this, adding functions later will be much easier.

Resources

Where to post your search results

Search results are more detailed than data. You may need to keep a historical record of searches, in which case you can add a reducer for searches:

 { searches: [ { searchTerm, searchDetails } ] } 

But even so, I don’t think I’ll save the search results. Saving results will limit functionality in a few cases.

  • When the user performs a search, a new book is added, you will not be able to find a new book in a subsequent search, if you do not restart the search (which denies the usefulness of storing the results).
  • The search should be quick, and storing results only speeds up repeated searches (which is likely to be less common).

So, I look at the results in the form of a detail - or, as James K. Nelson calls it, a “Control State” (read: 5 types of React Application State ). Your question does not indicate which view library (if any) you are using, but the concepts here should apply regardless of whether you use React or not.

Search results should be calculated by presentation. The view will receive user input, potentially extract the data (which will be added to the repository), run some filter function in the Redux state and display the results.

+1
source

There is a good model for reusable components in redux applications that come in handy. This assumes that the search uses the same api and logic in terms of abbreviation and one or more reusable components for logic.

In this example, I will use <AutoCompleteSearch /> and <FullSearch /> as user interface components, each of which can appear several times in the application or even on the same page.

The pattern used here is to assign a unique prop, designated as searchId: string for each component, for example. <AutoCompleteSearch searchId="wishlist" /> .

Then the component passes this identifier to the payload of all the actions related to the search, for example.

 { type: SEARCH, payload: { term: "Harry Potter", searchId: "wishlist", } } 

The search reducer is structured like a map with the searchId key. An example of a reduction state:

 { wishlist: { inProgress: true, searchTerm: "Harry Potter", results: [], }, reviews: { inProgress: false, searchTerm: "", results: [], }, } 

Then the reducer selects a part of the state for processing:

 const reducer = (state, action) => { switch(action.type) { case SEARCH: return { ...state, [action.payload.searchId]: { inProgress: true, searchTerm: action.payload.searchTerm, results: [], } }; default: return state; } 

For a ready-made library that uses this template to load data, see redux-autoloader .

+2
source

If you can reuse the search results, that is, the search query is the same or common to all components, then you are better off using a common component. If not, it is advisable to keep the search and search results separate and relevant to the context.

See examples Don shared with his todos pattern. He creates three different lists for All Todos , Completed Todos, and Active Todos .

0
source

I think the answer depends on your needs:

  • General search - for example. that were found in any context.
  • Uniqe search - for example, items that have been viewed in a specific context.

If you want a car driver in the first case, I would suggest combining all the search arrays and store the data in objects that give more deatils, such as searchContext, etc. If you want to autocomplete the second case, the search results for other contexts are irrelevant, therefore it is recommended to use separate arrays for any search context.

0
source

Source: https://habr.com/ru/post/1274879/


All Articles