具有 Redux 和 MVVM 架构的 React Hooks

kev*_*vin 6 mvvm reactjs redux react-hooks

我正在尝试更新我的 React 应用程序以使用 MVVM 作为架构模式。我最初使用 Hooks 和 Redux 构建它。我发现我找到的每个资源都使用 MVVM 中的类。此外,我找不到任何使用 Redux 来管理状态的东西。我不断看到 MobX 出现,但我正在尝试使用 Redux,因为我已经让它工作并且更好地理解了它。

这是迄今为止我找到的最好的资源: https: //medium.cobeisfresh.com/level-up-your-react-architecture-with-mvvm-a471979e3f21,不过......它使用类和mobX。

MVVM架构(据我了解)

模型

保存状态,在我的示例中是 Redux。

视图模型

充当模型和视图控制器之间的中间人。它处理业务逻辑并具有更新模型的能力。

视图控制器

视图之上的层调用从模型派生的行为以响应用户操作。还更新要在视图上显示的数据。

看法

虚拟组件,仅显示通过 props 或其他方式传入的数据并进行相应渲染。


我尝试将下面的逻辑分解为类似于 MVVM 的逻辑,但我的努力没有成功。这个组件非常简单,它只是<useSelector />在钩子中从 Redux 读取 () 状态,并在 return 语句中吐出信息。第二个文件是第一个组件的输出出现在屏幕上的位置。

import React from 'react';
import styled from 'styled-components';
import { useSelector } from 'react-redux';
import Skeleton from 'react-loading-skeleton';
import styles, { H300, PSpan } from '../../../ui/styles/variables';
import ProfileImage from '../../ProfileImage/ProfileImage';
const { colors, borders, effects } = styles;

const UserWidget = () => {
  const loading = useSelector((state) => state.auth.loading);
  const user = useSelector((state) => state.auth.user);

  if (loading) {
    return (
      <Content>
        <CustomSkeleton circle height={100} width={100} />
        <CustomSkeleton count={4} height={15} width={130} />
      </Content>
    );
  }

  return (
    <Content className="WIDGET">
      <ProfileImage email={user.email} />
      <Username>{user.username}</Username>
      <UserGlobalStats>
        <H300>117,311</H300>
        <PSpan>Points</PSpan>
        <H300>339</H300>
        <PSpan>Words Learned</PSpan>
      </UserGlobalStats>
    </Content>
  );
};

export default UserWidget;
Run Code Online (Sandbox Code Playgroud)
import React from 'react';
import CalendarWidget from '../../../components/_widgets/CalendarWidget/CalendarWidget';
import ModuleHeader from '../../../components/Module/Header/ModuleHeader';
import ModuleBody from '../../../components/Module/Body/ModuleBody';
import ModProgressWidget from '../../../components/_widgets/ModProgressWgt/ModProgressWgt';
import ButtonBlock from '../../../components/_widgets/ButtonBlock/ButtonBlock';
import { Page, LColumn, RColumn } from '../../../ui/styles/layouts';
import UserWidget from '../../../components/_widgets/UserWidget/UserWidget';

const OverviewPage = () => {

  return (
    <Page>
      <LColumn>
        <UserWidget />     <------ Here
        <CalendarWidget />
      </LColumn>
      <ModuleHeader difficulty="Beginners" language="French" />
      <ModuleBody layout="overview" />
      <RColumn>
        <ModProgressWidget />
        <ButtonBlock />
      </RColumn>
    </Page>
  );
};

export default OverviewPage;
Run Code Online (Sandbox Code Playgroud)

任何帮助,将不胜感激。我真的不想为了包含一些不同的技术而破坏和根除我一直在努力的一切。我看到一些神秘的答案说可以通过 MVVM 处理钩子。redux 肯定也太对了吧?

And*_*ata 2

我最近(2023年5月)一直在研究这个话题,我想我或多或少可以提供一个有效的答案。

在 React 之外的更传统的 MVVM 方法中,您会发现 MVVM 与 Observable 模式的数据绑定密切相关,就像在.NET中一样

但这在Android中也很常见,我找不到链接,但 iOS 上也有类似的情况。我对后两项技术没有太多经验,但我是根据我一直在做的一些研究得出的结论。

我认为这就是 OP 提到仅使用 MobX 找到示例的原因。但 React 的优点(和缺点)在于它非常灵活,我们可以使其适合Hooks 和 Redux的 MVVM架构方法,而不必采用Observable模式,我认为这是完全有效的。

让我们首先提供一个视觉辅助工具来更好地理解这个想法。

  +---------------------------+
  |          View             |
  +---------------------------+
             |   ^
             v   |
  +---------------------------+
  |        ViewModel          |
  +---------------------------+
             |   ^
             v   |
  +---------------------------+
  |          Model            |
  +---------------------------+
Run Code Online (Sandbox Code Playgroud)

上面的一个非常宽松的定义:

  • View:负责表示和表示逻辑
  • ViewModel:负责View和Model之间的通信,以及业务逻辑。
  • 模型:状态内容,或数据访问

主要需要注意的是关注点分离

现在,我们如何在遵循MVVM架构的同时使用 hooks 和 Redux?

该模型

// store.js
import { createStore } from 'redux';
import rootReducer from './reducers';

const store = createStore(rootReducer);

export { store };
Run Code Online (Sandbox Code Playgroud)
// reducers.js
const initialState = {
  counter: 0,
};

const rootReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return {
        ...state,
        counter: state.counter + 1,
      };
    case 'DECREMENT':
      return {
        ...state,
        counter: state.counter - 1,
      };
    default:
      return state;
  }
};

export { rootReducer };

Run Code Online (Sandbox Code Playgroud)

视图模型

// useCounterViewModel.js
import { useSelector, useDispatch } from 'react-redux';

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

  const increment = () => {
    dispatch({ type: 'INCREMENT' });
  };

  const decrement = () => {
    dispatch({ type: 'DECREMENT' });
  };

  return {
    counter,
    increment,
    decrement,
  };
};

export { useCounterViewModel } ;

Run Code Online (Sandbox Code Playgroud)
// CounterViewModel.js
import React from 'react';
import useCounterViewModel from './useCounterViewModel';

const CounterViewModel = () => {
  const viewModel = useCounterViewModel();

  return (
    <View
     counter={viewModel.counter}
     increment={viewModel.increment}
     decrement={viewModel.decrement}
    />
  );
};

export { CounterViewModel };

Run Code Online (Sandbox Code Playgroud)

风景

// CounterView.js
import React from 'react';

const CounterView = ({counter, increment, decrement}) => {
  return (
    <div>
      <h1>Counter: {counter}</h1>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
};

export { CounterView };
Run Code Online (Sandbox Code Playgroud)

此示例中发生了很多事情,但从概念上讲它适合MVVM架构。

最后一点是,如果您要使用一种模式,您应该明确每个实体的职责。因此,如果我们谈论 MVVM,那么您创建的模式及其名称应该反映模型-视图-视图模型概念。应该非常清楚什么是View、什么是ViewModel以及什么是Model,即使每个模型都是由较小的部分组成。

  • Model由Redux(store+reducer)组成
  • ViewModel由容器组件和Hook(业务逻辑)组成
  • View只是一个视图,相当愚蠢(也可以包含 UI 逻辑