MVVM Architecture in React Native using Redux

Sayali Shinde
5 min readMar 5, 2024

--

As a React Native Developer, I prioritize clean and well-structured code. Adopting the MVVM (Model-View-ViewModel) architecture enhances code maintainability and scalability. MVVM’s clear separation of concerns allows for easy addition or removal of features, promoting a more scalable application. It also facilitates independent unit testing for each component, ensuring reliable and efficient testing processes.

Layers of MVVM

  1. View: View is collection on elements where user can interact. For example there is a counter value which we can increment or decrement using buttons. It does not contain any logic like states, api calls etc. It is responsible for taking events from user and passing it to ViewModel for processing.
  2. ViewModel: The ViewModel is like the smart controller of your app, connecting the data (Model) with what you see on the screen (View). Its job is to make sure information is displayed correctly and handle any necessary behind-the-scenes logic. It contains properties, objects and methods.
  3. Model: The Model is responsible for managing the application’s data, business rules, logic, and behavior. It encapsulates the state and functionality of the application, ensuring that data is stored, processed, and manipulated in a consistent and meaningful way. In the context of MVVM, the Model is often separate from the ViewModel and is primarily concerned with the business logic and data storage.

Using MVVM Architecture in React Native application along with Redux

Lets start with our first layer that is View. Here we will see counter example to increment and decrement a counter using buttons. Folder Structure will be as follow:

View

Create a file named Counter.js in the src/screens directory. This file will have React Native components such as View, Text, Button, etc., enabling users to see and interact with the app's user interface. This component takes charge of handling user events and managing the visual aspects of the application. Your view doesn’t directly communicate with model for states updates, we have different component viewmodels which are responsible for getting data from model.

// Counter.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import useCounterViewModel from '../ViewModels/counterViewModel';

const Counter = () => {
const { count, increment, decrement } = useCounterViewModel();

return (
<View style={styles.container}>
<Text style={styles.counterText}>Counter: {count}</Text>
<View style={styles.buttonContainer}>
<Button color="pink" title="Increment" onPress={increment} />
<Button color="skyblue" title="Decrement" onPress={decrement} />
</View>
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
counterText: {
fontSize: 20,
marginBottom: 20,
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
width: '50%',
},
});

export default Counter;

ViewModel

The ViewModel is a vital controller that connects data (Model) with the user interface (View). It manages data-related logic, handles user interactions, and shapes how information is presented. This includes both data (ViewModel) and action logic, promoting a clean separation of concerns. The ViewModel acts as the brain behind the scenes, ensuring a smooth interaction between your app’s components.

In this example, Redux is chosen for state management. In Redux, actions play a role similar to ViewModels because they house the primary logic for fetching data from APIs, local storage, or other sources. However, it’s important to distinguish actions from the Model:

In the context of Redux, actions can be considered as ViewModels because they carry the main logic responsible for fetching and handling data. Actions act as controllers that manage the flow of data in the application.

Despite their ViewModel-like characteristics, Redux actions are not equivalent to the Model. They don’t directly change the application state or data.Actions trigger the changes by dispatching to reducers, which then update the state.

// counterViewModel.js
import { useSelector, useDispatch } from 'react-redux';
import { decrementCounter, incrementCounter } from '../redux/actions/counterActions';

const useCounterViewModel = () => {
const count = useSelector((state) => state.counter.count);
const dispatch = useDispatch();

const increment = () => {
dispatch(incrementCounter());
};

const decrement = () => {
dispatch(decrementCounter());
};

return {
count,
increment,
decrement,
};
};

export default useCounterViewModel;
// counterActions.js
export const incrementCounter = () => ({
type: 'INCREMENT_COUNTER',
});

export const decrementCounter = () => ({
type: 'DECREMENT_COUNTER',
});

Model

The Model is like the brain and memory of your application. It takes care of handling all the essential data, rules, and actions needed for your app to work. This includes storing information, performing calculations, and managing how the application behaves. In the MVVM architecture, the Model focuses on business logic and data storage, usually kept separate from the ViewModel. It’s like the store that holds all the application’s states and uses a reducer to update and manage those states.

// counterReducer.js
const initialState = {
count: 0,
};

const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT_COUNTER':
return { ...state, count: state.count + 1 };
case 'DECREMENT_COUNTER':
return { ...state, count: state.count - 1 };
default:
return state;
}
};

export default counterReducer;
// store.js
import { createStore, combineReducers } from 'redux';
import counterReducer from './reducers/counterReducer';

const rootReducer = combineReducers({
counter: counterReducer,
});

const store = createStore(rootReducer);

export default store;

Now let’s edit our entry point(App.js)for a React Native app. It imports the Redux Provider to make the store available to components, then renders the Counter component, serving as the main structure for managing state and data flow.

// App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './src/redux/store';
import Counter from './src/screens/Counter';

const App = () => {
return (
<Provider store={store}>
<Counter />
</Provider>
);
};

export default App;

Conclusion

Adopting the MVVM (Model-View-ViewModel) architecture in React Native brings clarity and maintainability to your codebase. MVVM separates data management (Model), presentation logic (ViewModel), and user interface (View), promoting modularity and ease of testing. This structured approach simplifies development, enhances code organization, and fosters a scalable foundation for building robust React Native applications.

GitRepo

--

--

Sayali Shinde

4+ years React Native experience. Proficient in full cycle, mentorship, and client relations. Delivered successful projects.