25、前端状态管理:从Redux到Relay/GraphQL

前端状态管理:从Redux到Relay/GraphQL

1. Redux在Home组件中的应用

在使用Redux构建应用时, action creator 函数是重要的组成部分,组件通过调用这些函数将负载(payload)分发到Redux存储中,最终导致状态的改变。有些操作在分发到存储之前需要先获取状态。下面以Neckbeard News应用的Home组件为例,展示如何将 action creator 函数传递给Redux存储。

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import { Map } from 'immutable';

const emptyMap = Map()
  .set(true, (<li style={listItemStyle}>...</li>))
  .set(false, null);

class Home extends Component {
  static propTypes = {
    articles: PropTypes.arrayOf(
      PropTypes.object
    ).isRequired,
    fetchingArticles: PropTypes.func.isRequired,
    fetchArticles: PropTypes.func.isRequired,
    toggleArticle: PropTypes.func.isRequired,
    filter: PropTypes.string.isRequired,
  }
  static defaultProps = {
    filter: '',
  }

  componentWillMount() {
    this.props.fetchingArticles();
    this.props.fetchArticles(this.props.filter);
  }

  onTitleClick = id => () =>
    this.props.toggleArticle(id);

  render() {
    const { onTitleClick } = this;
    const { articles } = this.props;
    return (
      <ul style={listStyle}>
        {emptyMap.get(articles.length === 0)}
        {articles.map(a => (
          <li key={a.id} style={listItemStyle}>
            <button
              onClick={onTitleClick(a.id)}
              style={titleStyle}
            >
              {a.title}
            </button>
            <p style={{ display: a.display }}>
              <small>
                <span>{a.summary} </span>
                <Link to={`articles/${a.id}`}>More...</Link>
              </small>
            </p>
          </li>
        ))}
      </ul>
    );
  }
}

export default connect(
  (state, ownProps) => Object.assign(
    state.get('Home').toJS(),
    ownProps
  ),
  dispatch => ({
    fetchingArticles: () => dispatch({
      type: 'FETCHING_ARTICLES',
    }),
    fetchArticles: (filter) => {
      const headers = new Headers();
      headers.append('Accept', 'application/json');
      fetch(`/api/articles/${filter}`, { headers })
        .then(resp => resp.json())
        .then(json => dispatch({
          type: 'FETCH_ARTICLES',
          payload: json,
        }));
    },
    toggleArticle: payload =>
      dispatch({
        type: 'TOGGLE_ARTICLE',
        payload,
      }),
  })
)(Home);

connect 函数用于将 Home 组件连接到Redux存储,它接受两个函数作为参数:
- 第一个函数将不可变的 state 对象映射为JavaScript对象,并将 ownProps 合并到Redux存储数据中。
- 第二个函数将 action creator 函数设置为组件的属性,通过 dispatch 函数将负载传递到存储。

下面是与 Home 组件相关的 reducer 函数:

import { fromJS } from 'immutable';
const typeMap = fromJS({
  FETCHING_ARTICLES: state =>
    state.update('articles', a => a.clear()),
  FETCH_ARTICLES: (state, payload) =>
    state.set(
      'articles',
      fromJS(payload)
        .map(a => a.set('display', 'none'))
    ),
  TOGGLE_ARTICLE: (state, id) =>
    state.updateIn([
      'articles',
      state
        .get('articles')
        .findIndex(a => a.get('id') === id),
      'display',
    ], display =>
      display === 'none' ?
        'block' : 'none'
    ),
});
export default (state, { type, payload }) =>
  typeMap.get(type, s => s)(state, payload);

这个 reducer 函数使用 typeMap 根据不同的操作类型来改变状态,逻辑清晰,系统中所有可能的变化都很明确。

2. Redux在移动应用中的状态管理

在React Native移动应用中也可以使用Redux。不过,与Web应用不同的是,移动应用的状态结构可能会有所不同。例如,Neckbeard News移动应用的初始状态如下:

import { fromJS } from 'immutable';
export default fromJS({
  Main: {
    title: 'All',
    component: 'articles',
  },
  Categories: {
    items: [
      {
        title: 'All',
        filter: '',
        selected: true,
      },
      {
        title: 'Local',
        filter: 'local',
        selected: false,
      },
      {
        title: 'Global',
        filter: 'global',
        selected: false,
      },
      {
        title: 'Tech',
        filter: 'tech',
        selected: false,
      },
      {
        title: 'Sports',
        filter: 'sports',
        selected: false,
      },
    ],
  },
  Articles: {
    filter: '',
    items: [],
  },
  Article: {
    full: '',
  },
});

虽然状态结构不同,但Redux的基本原理在移动应用和Web应用中是相同的,都是为了支持特定的组件和应用的独特实现方式。

3. Redux架构的可扩展性

Redux是实现大型React应用的一种很好的方式,它具有可预测性、声明性、单向数据流和无副作用等优点。但随着应用功能和复杂性的增加,应用中的组件会增多,导致实现速度变慢,难以把握整体情况。

4. Relay和GraphQL简介

Relay是另一种用于处理React应用状态的方法,它依赖于GraphQL语言来获取和修改资源。与Redux相比,Relay可以解决一些扩展性方面的限制,它将重点放在组件的数据需求上。

以下是Relay和GraphQL的一些基本术语:
| 术语 | 定义 |
| ---- | ---- |
| Relay | 一个管理应用数据获取和数据修改的库,提供高阶组件将数据注入应用组件 |
| GraphQL | 一种用于指定数据需求和数据修改的查询语言 |
| 数据依赖 | 表示某个React组件依赖特定数据的抽象概念 |
| 查询 | 数据依赖的一部分,用GraphQL语法表示,由封装的Relay机制执行 |
| 片段 | 较大GraphQL查询的一部分 |
| 容器 | 一个Relay React组件,将获取的数据传递给应用React组件 |
| 突变 | 一种特殊的GraphQL查询,用于改变远程资源的状态,Relay需要在完成后将此更改反映到前端 |

5. 声明性数据依赖

Relay使用 colocation 来描述声明性数据依赖,即数据依赖与使用数据的组件放在一起,这样可以清楚地看到组件需要的数据。例如,要显示用户的名字和姓氏,可以这样做:

const User = ({ first, last }) => (
  <section>
    <p>{first}</p>
    <p>{last}</p>
  </section>
);
const UserContainer = Relay.createContainer(User, {
  fragments: {
    user: () => Relay.QL`
      fragment on User {
        first,
        last,
     }
    `,
  },
});
6. 应用状态的突变

Relay突变是会导致系统副作用的操作,因为它们会改变UI关心的资源状态。而且,Relay突变会考虑到由于状态改变而对数据产生的副作用。例如,更改用户的名字可能会影响显示用户详细信息的屏幕,也可能影响显示多个用户的列表屏幕。

下面是一个更改用户年龄的突变示例:

class ChangeAgeMutation extends Relay.Mutation {
  static fragments = {
    user: () => Relay.QL`
      fragment on User {
        id,
      }
    `,
    viewer: () => Relay.QL`
      fragment on Viewer {
        id,
      }
    `,
  }
  getMutation() {
    return Relay.QL`mutation{changeAge}`;
  }
  getFatQuery() {
    return Relay.QL`
      fragment on ChangeAgePayload @relay(pattern: true) {
        user {
          age,
        },
        viewer {
          users,
        },
     }
    `;
  }
  getConfigs() {
    return [{
      type: 'FIELDS_CHANGE',
      fieldIDs: {
        user: this.props.user.id,
        viewer: this.props.viewer.id,
      },
    }];
  }
  getVariables() {
    return {
      age: this.props.age,
      id: this.props.todo.id,
    };
  }
}

这个突变包含以下几个部分:
- fragments :在突变发生之前,告诉突变组件使用了哪些数据。
- getMutation() :从服务器获取实际的突变操作,用于改变后端资源。
- getFatQuery() :让Relay确定执行此突变可能产生的副作用。
- getConfigs() :告诉Relay要执行的突变类型,以便进行相应的规划。
- getVariables() :将突变的参数发送到后端GraphQL服务器执行实际的突变操作。

7. GraphQL后端和微服务

Relay在浏览器端工作,需要将GraphQL查询发送到后端。可以使用Node.js和一些GraphQL库来实现GraphQL后端,创建一个描述所有数据类型、查询和突变的模式(schema)。

现代Web应用通常由微服务组成,GraphQL可以帮助解决后端由多个微服务组成时的可扩展性问题,通过模式来解析数据,为前端提供有意义的数据。

综上所述,Relay和GraphQL为React应用的状态管理提供了一种新的思路,虽然有一定的学习曲线,但在可扩展性方面有一定的优势。通过声明性数据依赖和显式的突变副作用处理,可以更高效地管理应用状态。同时,需要一个GraphQL后端来支持数据的获取和修改。

前端状态管理:从Redux到Relay/GraphQL

8. Relay与Redux的对比

Relay和Redux都是用于处理React应用状态的方法,但它们在很多方面存在差异,以下是两者的对比分析:
| 对比项 | Redux | Relay |
| ---- | ---- | ---- |
| 实现复杂度 | 随着应用规模扩大,代码量增多,实现复杂度增加,需要更多的 action creator reducer 等 | 减少了数据获取代码的复杂度,通过声明数据依赖来获取数据 |
| 可扩展性 | 当应用功能和复杂度增加时,会出现更多组件,实现速度变慢,难以把握整体情况 | 可以解决一些扩展性方面的限制,将重点放在组件的数据需求上 |
| 数据获取方式 | 需要手动编写 action creator 函数来获取数据,分散在多个模块中 | 使用声明性数据依赖,数据依赖与使用数据的组件放在一起,清晰明了 |
| 副作用处理 | 主要关注单向数据流和可预测的状态转换,对副作用的处理相对间接 | 显式处理突变的副作用,能更好地应对状态改变对数据产生的影响 |

9. 完整流程分析

下面通过mermaid流程图展示从Redux到Relay/GraphQL在前端状态管理中的完整流程:

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px

    A([开始]):::startend --> B{选择状态管理方式}:::decision
    B -->|Redux| C(定义action creator函数):::process
    C --> D(编写reducer函数):::process
    D --> E(使用connect函数连接组件和store):::process
    E --> F(组件调用action creator函数改变状态):::process
    B -->|Relay/GraphQL| G(定义声明性数据依赖):::process
    G --> H(创建Relay容器组件):::process
    H --> I(编写GraphQL查询和突变):::process
    I --> J(处理突变的副作用):::process
    F --> K([结束]):::startend
    J --> K
10. 实际操作步骤总结

无论是使用Redux还是Relay/GraphQL,在实际开发中都有相应的操作步骤:
- Redux操作步骤
1. 定义 action creator 函数,用于创建不同类型的 action
2. 编写 reducer 函数,根据 action 的类型来改变状态。
3. 使用 connect 函数将组件连接到Redux存储,传递 action creator 函数和状态。
4. 在组件中调用 action creator 函数来触发状态的改变。
- Relay/GraphQL操作步骤
1. 定义声明性数据依赖,使用 colocation 将数据依赖与组件放在一起。
2. 创建Relay容器组件,通过 createContainer 函数指定组件的数据依赖。
3. 编写GraphQL查询和突变,使用 Relay.QL 语法。
4. 处理突变的副作用,通过 getConfigs 等函数告诉Relay如何处理。
5. 确保有一个GraphQL后端,创建描述所有数据类型、查询和突变的模式。

11. 总结与展望

在前端开发中,状态管理是一个至关重要的环节。Redux和Relay/GraphQL都为React应用的状态管理提供了有效的解决方案。Redux以其可预测性和单向数据流的特点,适用于大多数规模的应用,但在处理大规模复杂应用时可能会遇到一些挑战。而Relay/GraphQL通过声明性数据依赖和显式的突变副作用处理,在可扩展性方面具有一定优势,能够更好地应对现代Web应用的复杂性。

在实际项目中,开发者可以根据项目的规模、复杂度和具体需求来选择合适的状态管理方式。对于小型项目,Redux可能是一个简单直接的选择;而对于大型复杂项目,Relay/GraphQL可能更能发挥其优势。同时,随着前端技术的不断发展,状态管理的方法也会不断演进,开发者需要持续学习和探索,以找到最适合自己项目的解决方案。

跟网型逆变器小干扰稳定性分析与控制策略优化研究(Simulink仿真实现)内容概要:本文围绕跟网型逆变器的小干扰稳定性展开分析,重点研究其在电力系统中的动态响应特性及控制策略优化问题。通过构建基于Simulink的仿真模型,对逆变器在不同工况下的小信号稳定性进行建模与分析,识别系统可能存在的振荡风险,并提出相应的控制优化方法以提升系统稳定性和动态性能。研究内容涵盖数学建模、稳定性判据分析、控制器设计与参数优化,并结合仿真验证所提策略的有效性,为新能源并网系统的稳定运行提供理论支持和技术参考。; 适合人群:具备电力电子、自动控制或电力系统相关背景,熟悉Matlab/Simulink仿真工具,从事新能源并网、微电网或电力系统稳定性研究的研究生、科研人员及工程技术人员。; 使用场景及目标:① 分析跟网型逆变器在弱电网条件下的小干扰稳定性问题;② 设计并优化逆变器外环与内环控制器以提升系统阻尼特性;③ 利用Simulink搭建仿真模型验证理论分析与控制策略的有效性;④ 支持科研论文撰写、课题研究或工程项目中的稳定性评估与改进。; 阅读建议:建议读者结合文中提供的Simulink仿真模型,深入理解状态空间建模、特征值分析及控制器设计过程,重点关注控制参数变化对系统极点分布的影响,并通过动手仿真加深对小干扰稳定性机理的认识。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值