📜 ⬆️ ⬇️

Redux architecture. Yes or no?

The author of the material, the translation of which we are publishing today, says that he is part of the Hike messenger team, which deals with new features of the application. The goal of this team is to translate into reality and explore ideas that users might like. This means that developers need to act quickly, and that they often have to make changes to the innovations they explore, which are aimed at making the work of users as convenient and enjoyable as possible. They prefer to conduct their experiments using React Native, as this library speeds up development and allows you to use the same code on different platforms. In addition, they use the Redux library.



When developers from Hike start working on something new, then, when discussing the architecture of the solution under study, they have several questions:


As a matter of fact, Redux helps to find the right answers to all these questions. The Redux architecture helps to separate the state of the application from React. It allows you to create a global repository located at the top level of the application and providing state access for all other components.
')

Separation of responsibilities


What is a “division of responsibility”? This is what Wikipedia says about this: “In computer science, the division of responsibilities is the process of dividing a computer program into functional blocks that overlap each other’s functions as little as possible. In a more general case, the division of responsibilities is the simplification of a single process for solving a problem by dividing it into interacting processes to solve subtasks. ”

The Redux architecture allows you to implement the principle of separation of responsibilities in applications, breaking them into four blocks, presented in the following figure.


Redux architecture

Here is a brief description of these blocks:


Consider the Redux architecture by example.

What if different components need the same data?


The Hike app has a screen displaying a list of the user's friends. At the top of this screen displays information about their number.


Hike friend info screen

There are 3 React components here:


Here is the friendsContainer.js file code that illustrates the above:

 class Container extends React.Component {   constructor(props) {     super(props);     this.state = {       friends: []     };   }   componentDidMount() {     FriendsService.fetchFriends().then((data) => {       this.setState({         friends: data       });     });   }   render() {     const { friends } = this.state;     return (       <View style={styles.flex}>       <FriendsHeader count={friends.length} text='My friends' />       {friends.map((friend) => (<FriendRow {...friend} />)) }       </View>     );   } } 

An absolutely obvious way to create such an application page is to load friend data into the container component and pass it as properties to the child components.

Let us now consider that this data about friends may be needed in some other components used in the application.


Chat Screen in Hike App

Suppose an application has a chat screen that also contains a list of friends. It can be seen that the same data is used on the screen with a list of friends and on the chat screen. What to do in this situation? We have two options:


Both of these options are not so attractive. Now let's look at how our problem can be solved using the Redux architecture.

Using redux


Here we are talking about the organization of work with data using the repository, action creators, reducers and two user interface components.

â–Ť1. Data store


The repository contains downloaded data about the user's friends. This data can be sent to any component if it is needed there.

â–Ť2. Action creators


In this case, the creator of the action is used to dispatch events to save and update data about friends. Here is the code for the friendsActions.js file:

 export const onFriendsFetch = (friendsData) => { return {   type: 'FRIENDS_FETCHED',   payload: friendsData }; }; 

â–Ť3. Reductors


Reducers expect the arrival of events that represent dispatched actions, and update the data about friends. Here is the code for the friendsReducer.js file:

 const INITIAL_STATE = {      friends: [],   friendsFetched: false }; function(state = INITIAL_STATE, action) {   switch(action.type) {   case 'FRIENDS_FETCHED':       return {           ...state,           friends: action.payload,           friendsFetched: true       };   } } 

â–Ť4. Friend List Component


This container component views friends and updates the interface as they change. In addition, he is responsible for downloading data from the storage in the event that he does not have them. Here is the code for the friendsContainer.js file:

 class Container extends React.Component {   constructor(props) {     super(props);   }   componentDidMount() {     if(!this.props.friendsFetched) {       FriendsService.fetchFriends().then((data) => {         this.props.onFriendsFetch(data);       });     }   }   render() {     const { friends } = this.props;     return (       <View style={styles.flex}>       <FriendsHeader count={friends.length} text='My friends' />       {friends.map((friend) => (<FriendRow {...friend} />)) }       </View>     );   } } const mapStateToProps = (state) => ({ ...state.friendsReducer }); const mapActionToProps = (dispatch) => ({ onFriendsFetch: (data) => {   dispatch(FriendActions.onFriendsFetch(data)); } }); export default connect(mapStateToProps, mapActionToProps)(Container); 

â–Ť5. Component that displays the chat list


This container component also uses data from the repository and responds to their updating.

About the implementation of the Redux architecture


In order to bring the above described architecture to a working state, it may take a day or two, but when changes need to be made to the project, they are made very simply and quickly. If you need to add a new component to your application that uses data about friends, you can do this without having to worry about data synchronization or about having to redo other components. The same applies to the removal of components.

Testing


When using Redux, each application block is independently testable.
For example, each component of the user interface can be easily subjected to unit testing, since it is independent of the data. The point is that a function representing such a component always returns the same representation for the same data. This makes the application predictable and reduces the likelihood of errors occurring during data visualization.

Each component can be comprehensively tested using a variety of data. Such testing helps to reveal hidden problems and contributes to ensuring the high quality of the code.

It should be noted that not only components responsible for data visualization, but also reducers and action creators can be subjected to independent testing.

Redux is great, but using this technology we encountered some difficulties.

Difficulty Using Redux


â–Ť excess template code


In order to implement the Redux architecture in an application, you have to spend a lot of time, encountering all sorts of strange concepts and entities.

These are the so-called sledges (thunks), reducers (reducers), actions (actions), intermediate software layers (middlewares), these are mapStateToProps and mapDispatchToProps , and much more. It takes time to learn all this, but it takes practice to learn how to use it properly. There are a lot of files in a project, and, for example, one minor change to a component to visualize data may result in the need to make changes to four files.

Red Redux vault is singleton


In Redux, the data warehouse is built using the Singleton pattern, although components may have multiple instances. Most often this is not a problem, but in certain situations such an approach to data storage can create some difficulties. For example, imagine that there are two instances of a certain component. When data changes in any of these instances, the changes also affect the other instance. In certain cases, this behavior may be undesirable; it may be necessary for each instance of the component to use its own copy of the data.

Results


Recall our main question, which is whether to spend time and effort on the implementation of the Redux architecture. We, in response to this question, say Redux "yes." This architecture helps save time and effort when developing and developing applications. Using Redux makes it easier for programmers to make frequent changes to the application, and makes testing easier. Of course, the Redux architecture provides for a considerable amount of template code, but it contributes to breaking the code into modules that are convenient to work with. Each such module can be tested independently of the others, which contributes to the identification of errors at the design stage and allows for high quality programs.

Dear readers! Do you use Redux in your projects?

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


All Articles