admin管理员组文章数量:1287268
State management in React Native is a well-debated topic, with Redux often being the default choice. However, for developers coming from native mobile development (Kotlin/Swift), Redux can feel cumbersome with its actions, reducers, and dispatching.
As someone experienced in native development, I built my own ViewModel-based state management system in React Native to align better with the MVVM (Model-View-ViewModel) architecture commonly used in Android and iOS. This approach keeps things modular, structured, and easy to scale.
I'll walk you through why I built this system, how it works, and how it compares to Redux.
Why Not Redux?
While Redux is powerful, it comes with some drawbacks:
- Too much boilerplate: Actions, reducers, dispatching-it's a lot for managing simple state.
- Overhead for screen-specific state: Redux is global by design, but sometimes we only need state for a single screen.
- Harder integration with Dependency Injection (DI): Redux doesn't naturally fit DI patterns used in native apps.
For these reasons, I built a ViewModel-based approach that solves these problems while keeping things modular and scalable.
The Core Concept: ViewModels in React Native
In native development, ViewModels handle UI logic and expose state for the View (UI) to consume. I recreated this concept in React Native using React Context and hooks, making it easy to encapsulate both global and screen-specific state.
Here are 3 examples about how I use this ViewModel system:
- RootViewModel – Handles app-wide state (e.g., user authentication, theme settings).
- TodoListViewModel – A standard ViewModel that lists todo items.
- TodoItemViewModel – A ViewModel with data, that takes a todo item ID from the route to manage state for a single item.
This shows how you can have a shared ViewModel, or ViewModels scoped to screens, using data from route or not.
Base definitions
I use the following types to simplify ViewModel creation and usage:
export type ViewModel = React.FC<{ children?: React.ReactNode }>
export type ParamsViewModel<T> = React.FC<{ children?: React.ReactNode } & T>
export const withViewModel: <T extends object>(
Component: React.FC<T>,
ViewModel: ViewModel,
) => React.FC<T> = (Component, ViewModel) => (props) => {
return <ViewModel>
<Component {...props}/>
</ViewModel>
}
export const withParamsViewModel: <T extends object>(
Component: React.FC<T>,
ViewModel: ParamsViewModel<T>,
) => React.FC<T> = (Component, ViewModel) => (props) => {
return <ViewModel {...props}>
<Component {...props}/>
</ViewModel>
}
Then I can wrap my components with ViewModel like this:
const MyComponent: React.FC = () => {
const { data } = useSomeViewModel()
return <></>
}
export default withViewModel(MyComponent, SomeViewModel)
RootViewModel: Managing Global State
My RootViewModel acts as a global store but without the complexity of Redux. It provides essential global data (e.g., user info) and ensures that any ViewModel can access it.
Example:
const RootViewModelContext = createContext({
user: null as User | null,
login: async (credentials: Credentials) => {},
})
export const RootViewModel: ViewModel = ({ children }) => {
const [user, setUser] = useState<User | null>(null)
const { authRepository } = useDI()
const login = async (credentials: Credentials) => {
const newUser = await authRepository.login(credentials)
if (newUser) setUser(newUser)
}
return <RootViewModelContext.Provider value={{ user, login }}>
{children}
</RootViewModelContext.Provider>
}
export const useRootViewModel = () => useContext(RootViewModelContext)
Now, any component or ViewModel can access global state via useRootViewModel()
while keeping setUser
private.
TodoListViewModel: Managing a List of Todo Items
This ViewModel is not parameterized and is used to manage a list of todos.
Example:
const TodoListViewModelContext = createContext({
todos: [] as Todo[],
fetchTodos: async () => {},
})
export const TodoListViewModel: ViewModel = ({ children }) => {
const [todos, setTodos] = useState<Todo[]>([])
const { todoRepository } = useDI()
const fetchTodos = async () => {
const data = await todoRepository.getTodos()
setTodos(data)
}
useEffect(() => {
fetchTodos()
}, [])
return <TodoListViewModelContext.Provider value={{ todos, fetchTodos }}>
{children}
</TodoListViewModelContext.Provider>
}
export const useTodoListViewModel = () => useContext(TodoListViewModelContext)
TodoItemViewModel: Managing a Single Todo Item
This ViewModel takes a parameter (todo item ID) from navigation and manages the state of a single todo item.
Example:
export const TodoItemViewModel: ParamsViewModel<NativeStackScreenProps<TodoNavigationRoutes, "TodoItemScreen">> = ({
children, navigation, route
}) => {
const [todo, setTodo] = useState<Todo | null>(null)
const { todoRepository } = useDI()
useEffect(() => {
todoRepository.getTodo(route.params.todoId).then(setTodo)
}, [route.params.todoId])
return <TodoItemViewModelContext.Provider value={{ todo }}>
{children}
</TodoItemViewModelContext.Provider>
}
Why This Works Better Than Redux for Screens
- Encapsulates state per screen instead of storing everything globally.
- Uses
route.params
directly, avoiding unnecessary Redux actions. - Works seamlessly with Dependency Injection (DI).
Final Comparison: My ViewModel System vs. Redux
Feature | ViewModel System | Redux |
---|---|---|
State Location | Global (RootViewModel ) + per-screen (ParamsViewModel ) |
Global Redux store |
Performance | Optimized with Context splitting, useMemo , and useCallback |
Optimized with useSelector |
Boilerplate | Minimal, simple DI-friendly architecture | Requires reducers, actions, and dispatch logic |
Navigation Support | Uses route.params naturally |
Requires extra logic to sync navigation state |
Best for | Modular, self-contained apps | Large-scale apps with cross-screen state |
本文标签: mvvmUsing ViewModels in React Native (instead of Redux)Stack Overflow
版权声明:本文标题:mvvm - Using ViewModels in React Native (instead of Redux) - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741306089a2371376.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论