Data modelling my store in Redux - reactjs

Consider this:
I have an application that is going to end up being pretty large. It is a dashboard which will give you access to various utilities, one of which being a todo app.
If I was just going to build just a todo app, then my state object would look like so:
{ todos:[], completed:false, todoInput:''};
todoInput would be tied to a form field and and upon clicking add, it would alter the todos array and toggle the completed field. So my combineReducers() function would look like this.
combineReducers({todos,completed,todoInput});
This would make sense because all the state is relevant to the todo App because there is JUST a todo app.
Now because I am building a much more complicated application which also has a todo app, this is how my state would potentially look like:
{
otherState:'',evenMoreState:[]',evenMore:{},
todo:{ todos:[], completed:false, todoInput:''}
}
As you can see I have separated todos into a separate object now, so it is encapsulated and more organised. So I have 2 questions.
1) Is this a good idea? It seems like the logical move because my application will grow in size and I don't want all the pieces of state floating around as properties to the main state object. Have I gone about this correctly?
2) My combine reducers (as far as I know) cannot take a nested object. So it will now look like this.
combineReducers({ otherState,evenMoreState,evenMore,todo})
so now my reducer compositions will have to be done inside the reducer which handles the todo state. Is there a better/different way to do this?
Thanks

Yes, you're absolutely on the right track. It's also worth noting that you can use combineReducers multiple times, such as:
const rootReducer = combineReducers({
otherState : otherStateReducer,
todos : combineReducers({
todos : todosReducer,
completed : todosCompletedReducer,
todoInput : todoInputReducer
})
The overall todos section could be be defined separately, and imported and referenced in the top-level combineReducers call if desired.
});
You may want to read through the Redux docs section on "Structuring Reducers" for more information on ways to organize reducer logic, as well as the Redux FAQ on organizing nested state. In addition, the Redux Techniques and Redux Architecture sections of my React/Redux links list have links to a variety of articles about building real-world Redux applications.

Related

Do I have to save every React component state property to the Redux store?

I have read multiple articles on the need to use Redux and have built two fully-functioning React+Redux applications. I have even posted the question on Quora I still cannot have a final answer to my question:
Do I have to save every component state property to the Redux store?
The first project, I have built by following a tutorial where he basically saves everything to the store.
Here's the Github link.
Since I was learning React and Redux, I did not question this approach and went on with it. But, it does seem somewhat unnecessary to save everything to the store
For example, there's an action that saves the comment data to the store:
postActions.js
// Add Comment
export const addComment = (postId, commentData) => dispatch => {
dispatch(clearErrors());
axios
.post(`/api/posts/comment/${postId}`, commentData)
.then(res =>
dispatch({
type: GET_POST,
payload: res.data
})
)
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
And it is called like this:
CommentForm.js
onSubmit(e) {
e.preventDefault();
const { user } = this.props.auth;
const { postId } = this.props;
const newComment = {
text: this.state.text,
name: user.name,
avatar: user.avatar
};
this.props.addComment(postId, newComment);
this.setState({ text: '' });
}
If I were working on my own project, I would've kept the message data stored locally at the component level:
The second project was a personal project, where the only data I saved in the store, is the user account information because I would need it in different components throughout the app to send it in some backend API requests.
All the other components are basically independent or the flow between them does not go beyond two or three components. So I really could not see why I would make myself code all the actions, reducers...etc for all of the components. So I simply pass the props and functions between components in the plain old react way of doing things.
Most of the answers that I found do not go into this specific detail mentioned in my question. All of them talk from a high-level perspective.
Before going ahead and working on other projects, I would like to :
A clear answer to my question
Whether the approach I used for my personal project is okay. In other words, can I use Redux simply for the user account information and for the rest of the components not use it?
I just want to clear this confusion so that when I am using Redux, I am 100% sure, I am using it because I actually need it.
Do I have to save every component state property to the Redux store?
Short answer: No you don't.
Longer answer: To quote Dan Abramov on a similar question:
Use React for ephemeral state that doesn't matter to the app globally and doesn't mutate in complex ways. For example, a toggle in some UI element, a form input state.
Use Redux for state that matters globally or is mutated in complex ways. For example, cached users, or a post draft.
There is nothing wrong with the approach taken in your personal project. Redux is great for storing/sharing global application state, such as the user info you describe.
Before putting state into Redux I'd ask:
Will this state be consumed by other components independent to this one?
If the answer to #1 is yes: then ask how often?
If the answer to #2 is frequently: then ask is a single source of truth (the Redux store) the best way to share this particular piece of state? Would
other techniques (hooks / render props / higher order components) be more appropriate?
Another quote from Dan in the same linked thread is:
If it gets tedious and frustrating don’t be afraid to put state into the components. My point is that use single state tree unless it is awkward, and only do this when it simplifies things for you rather than complicates them. That’s the only guideline.
The mantra Yagni (You Aren't Gonna Need It) springs to mind.
If you're unsure whether state should be abstracted from a component into Redux, then the chances are it's too early todo so. This helps avoid making design decisions too early, whilst keeping your Redux state lean and intentional (i.e: not convoluted with unnecessary single use concerns).
Ultimately the cost of putting state into Redux needs to pay off.

Redux-way in the real life

I want to ask the community about an ideological problem.
Lets imagine todo-list on react/redux, you have single state where todoItems array is served. But now lets imagine I want to have few components on the page that are render todoItems with different UI. And I need to update each these components on CRUD of todoItems. What is your architectural approach of this issue? Don't forget we have a large database and we can get todoItems with pagination only.
Update:
Lets make it clear. When we implement redux life cycle with this UI we have 2 options:
1) Serve one array of todoItems into singleton redux state object.
Advantages: all our components will updates by object changing.
Problems: we can't get ALL data from our database, but have to show different paginated/filtered data, so we can't implement pagination/filtering on frontend-side. We have a few different components and the have to render different objects collection. So it doesn't fit.
2) We can use different keys into our global redux state.
Advantages: we can independently get data for each component
Problems: other components will not feel when object changing in one of them. In this case we have to write custom code.
I just want to know maybe I'm missing something and we have other option or maybe someone have good architectural approach to this problem.
I bet your complications come from the point of view which unfortunately quite common among redux community: trying to keep redux shape as close to UI shape as possible.
Try no to think about redux state as a substitute for the Component states. What redux should know about is actual todos only (id, title, date of creation, etc.). Let Component-specific data like pagination stuff live in Components state. When user goes to next page in one of the Components what should be updated is this Component state (pageNumber, from, to, amount, etc.). redux should be updated only in case necessary todos are missing.
The useful analogy is to thinking about your redux as good old SQL-database: redux store state is data itself, selectors and actions are queries and stored procedures, React Components are views with selected data.
Update: Ok, seems like what you are looking for is state normalization. Separate todos details from the lists of ids. This way updates of todo fields will be sensed by all the Components. On the other hand you'll be able to keep separate collections of todos in different Components. Namely make state look like this:
{
funnyTodos: [ 'id1', 'id2' ],
boringTodos: [ 'id3', 'id4' ],
recentlyDoneTodos: [ 'id1' ],
todos: {
id1: { name: .... },
id2: { name: .... },
id3: { name: .... },
id4: { name: .... },
}
}
Implementing pagination in this case is just a matter of getting list of todos ids for the next page from back-end and then loading missing todos for given ids.

React & Redux with dynamic elements

I am currently developing an analytics dashboard in React/Redux that is similar to this:
Users of the dashboard will be able to add and remove tiles to customise the dashboard to their own needs, and the configuration of the tiles is stored and retrieved in an API.
The storing of the data for the configuration of tiles seems to fit well with the global state model:
On load, the dashboard component dispatches a 'loadTiles' action
The action fetches the tiles data and passes it to the 'tiles' reducer
From there it goes into the store/global state.
In mapStateToProps, the data is accessed from state.app.tiles
However, a problem arises when populating the data for each tile. The number of tiles and nature of the data is dynamic, so reducers can't be set up ahead of time.
This could be solved by each component managing their own state (as in pure/traditional React using componentWillMount etc) but this will violate some of the architectural principals that have been laid out for the rest of the project (ideally everything is to be managed in global state).
The only way I can see of storing the data is global state would be to have an analytics with a dynamic array of the various data sets, which sounds messy to me.
Is local component state the best solution here? or can this be done in global state cleanly? Are there any example of Redux using queries that are dynamically specified?
One thing you can do is the usage of an ID for each Tile. So your state could look like that:
{
tiles: {
tile1: {},
…
tile100: {}
}
}
Than, in the mapStateToProps() function you can use own props like so:
function mapStateToProps(state, ownProps) {
//test if it exists
if (state.tiles[ownProps.id]) {
return { tileData: state.tiles[ownProps.id] }
}
else
{
return { tileData: <default state> }
}
}
The important part is to hand over a unique ID for each tile, when those are created, one way could be that:
<Tile id={uuid()} other="stuff" />
whereby the uuid() method can be created as described here
I once had an similar issue, have a look here if you want to see a more complicated solution using an higher order component (its my own unaccepted answer). All in all, the above is the simplest solution IMHO.

How to connect each element of array individually by using react redux

The current approach is to connect whole book list into Book List Component. However, it is not an efficient to render huge components by changing only several fields in state. I dont know how to map each book component connect to each individual Book state.
The project is using redux. The application global state like this
{
"books": [
{
"field1": "field1",
"field2": "field2",
"field3": "field3",
} ...
]
}
In the Book List component, we connect list with it by using react redux
export default connect(state => ({
books: state.books
}))(BookListComponent);
Now the requirement changes, fields in book will not be changed frequently. The issue is, if one field is changed, it will update the whole BookListComponent. That is not performant component I am expecting.
I would like to push connect logic down to individual Book Component in order to use reselect
The state doesnt have index of the book, I dont know how to write connect to make it work
export default connect(state => ({
books[index]: state.books[index]
}))(BookListComponent);
Thanks for advance and all options are welcome
Your second example is almost correct. A mapState function can be declared to take two parameters instead of one. If two parameters are declared, Redux will call that mapState function with the props that were given to the wrapper component. So, the mapState function for a list item would be:
const mapState = (state, ownProps) => {
const book = state.books[ownProps.index];
return {book}
}
My blog post Practical Redux, Part 6: Connected Lists, Forms, and Performance shows some examples of how to do that, and also discusses the key considerations for application performance in Redux.
You can connect any component you like. There is no hard and fast rule that you can only connect top level components.
While there is a performance hit to connecting top level components and passing the props down I have not witnessed it have a noticeable detrimental effect. The benefit in being able to trace the data through the code is always worth it in my opinion. There is a discussion on it here.
React updates only changed fields
While it's true that render function is called each time you update a book, that doesn't mean that the whole list is re-rendered.
Remember, that in React we are using Virtual DOM. It allows us to update only the elements that are actually changed.
Take a look at this article (it's short and has working code example on codeopen) https://facebook.github.io/react/docs/rendering-elements.html
and this one (a little more detailed)
https://facebook.github.io/react/docs/state-and-lifecycle.html
If you have read those articles, you know that all you need is to inspect your app and see what is actually rendered at each change.

React with Redux, how to design my state

My website has 3 pages, e.g. www.example.com.au/invoices, www.example.com.au/customers and www.example.com.au/suppliers.
In this case, how to organize the global Redux state, I mean the structure.
should it look like below?
state = {
invoices: {
//invoices related data
},
customers: {
//customers related data
},
suppliers: {
//suppliers related data
}
}
That looks a good start. I find it's a good idea to try and keep the state as normalized as possible - try thinking about it as a relational database.
This is a great article:
https://medium.com/javascript-scene/10-tips-for-better-redux-architecture-69250425af44#.9c678jwib
as is
http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
state = {
invoices: {
//invoices related data
},
customers: {
//customers related data
},
suppliers: {
//suppliers related data
}
}
this looks good.
If you're on invoices page and if you won't be accessing state.suppliers or state.customers, then you don't need to combineReducers all of them together.
You can lazy load reducers (dynamically and use store.replaceReducer to inject them into store). This saves you some bytes ;)
Some tips:
Separate business logic from views (components) and reducers.
Identify core reducers (those which are needed throughout the app) and others. Only create initial store with core reducers.
Split reducers and action creators into different files and lazy load reducers as required.
It is up to you on how to store. But a more convenient way to do is, having separate file for each of them and combine them as a single store.
i.e, your folder structure looks some what like this,
reducers/
|---- index.js
|---- customers.js
|---- invoices.js
|---- suppliers.js
Inside each of customers.js, invoices.js and suppliers.js, write their own reducers in each of them and combine them to a single large store in the index.js file using combineReducers() method from redux
Basically the flow is,
Write separate small-small reducers.
Import all the reducers in a single file(index.js).
Using combineReducers() combine them into a single large reducer and export it to store.
Hope it helps! :)

Resources