将reducers和actions绑定到非根组件

时间:2016-08-31 01:07:43

标签: javascript reactjs redux react-redux

背景

我对Angular 1.x有一年半的经验,转向React / Redux进行Angular不适合的项目。我已经读过某个地方,React是来自Angular的“基本上所有指令”,这太棒了,因为我认为指令是Angular最强大的功能。然而,在React / Redux与Angular中创建应用程序的心态也非常不同(比如创建数据主干然后再代表它的想法,而不是Angular的集中式方法)。我有很多困难要理解。

设置

应用程序正在Electron中编写,使用以下作为树苗:https://github.com/chentsulin/electron-react-boilerplate

我的root reducer看起来像这样:

root: {
  Home: Object, // Basic homepage tab
  Gallery: [ // Gallery tab
    { // A single instance of "photo" object
      filename: String,
      metadata: Object,
      error: Object
    }
  ]
}

Gallery具有打开目录的功能。它扫描用户选择的目录并在那里查找照片,接收一组文件名。然后它应该将每张照片作为一个组件呈现,每张照片在用文件名初始化后,应该启动自己的阅读图片元数据并在页面上呈现自己信息的过程。

我得到了这一点,我可以在页面上呈现所有文件名没问题。但是,我需要将文件名转换为渲染和处理过的照片。那就是我遇到麻烦的地方。

问题

照片有一些属性,如上图所示。它还有一些操作(例如newupdatedelete等。属性和操作需要绑定到组件。通常,对于页面大小的组件,这是在JS文件中完成的,该文件执行所有connectbindActionCreators调用。然后将此JS文件包含在反应路由器中,并显示为页面。

照片不是网页。它们意味着是可重用的组件,页面上有很多组件。如何正确附加照片的所有属性和操作?

1 个答案:

答案 0 :(得分:3)

看起来你可能会在模型/动作/减速器上划线。与Angular和其他框架不同,你的模型应该是“愚蠢的”,因为它只是数据;您的GalleryPhoto对象不应包含方法。相反,您的视图会触发您的reducer用于更新存储(包含模型)的操作,然后将其读取并推回到视图中。与MVVM(MVV *?)不同,您的模型(存储状态)不必模仿组件的结构;相反,它只是维护纯数据。因此,拥有HomeGallery属性可能没有意义。

让我们重新想象您的减速机:

const initialState = {
  photos: []
};

export default function reducer(state = initialState, action) {
  switch (action.type) {
    case 'DELETE_PHOTO':
      return { 
        photos: state.photos.filter(photo => photo.filename !== action.payload.filename) 
      };
    // etc.
  }

  return state;
}

现在您的观点可能是这样的:

import deletePhoto from '../actionCreators/deletePhoto';

function Photo({ filename, onDelete }) {
  return (
    <div>
      <img src={filename}/>
      <button onClick={() => onDelete(filename)}>Delete</button>
    </div>
  );
}

function PhotoGallery({ photos, deletePhoto }) {
  return (
    <div>
    {
      photos.map(photo => <Photo filename={photo.filename} onDelete={deletePhoto}/>);
    }
    </div>
  );
};

const mapStateToProps = state => ({ photos: state.photos });
const mapActionsToProps = { deletePhoto };

export default connect(mapStateToProps, mapActionsToProps)(PhotoGallery);

...然后您的动作创建者deletePhoto可能类似于:

export default function deletePhoto(filename) {
  return { type: 'DELETE_PHOTO', payload: { filename } };
}

建议将缩减器分解为更精细的碎片,但不要通过扩充模型来实现,而是使用combineReducers之类的东西。

我建议你试着忘掉MVVM并真正理解通量模式。我并不是说这是一个更好的模式,但如果你尝试使用不同的模式和redux,那么你真的会反对这种模式。