react中的状态机

Mixing React and state machines is a great productivity boost for you as a developer. It also improves the usually shaky developers/designers collaboration.

混合使用React和状态机可以极大地提高您作为开发人员的工作效率。 它还可以改善通常不稳定的开发人员/设计人员之间的协作。

The state machine concept is very simple: a component can be in one state at a time and has a finite number of states.

状态机的概念非常简单:一个组件一次只能处于一种状态,并且具有有限数量的状态。

How is this helpful in UI development you say?

您说这对UI开发有什么帮助?

问题 (The problem)

Let us consider a simple text edition component like the very poorly styled one below:

让我们考虑一个简单的文本编辑组件,例如以下样式很差的组件:

The possible “states” of the such a component are (from left to right):

此类组件的可能“状态”为(从左到右):

  • Display value

    显示值
  • Edit value

    编辑值
  • Save in progress

    进行中保存
  • Saving error

    保存错误

A straightforward shape for the component model has 5 properties:

组件模型的简单形状具有5个属性:

state: {
  processing: true, // Will be true when saving is in progress
  error: null,      // Will be not null when a save error occurs
  value: null,      // The read only display value
  edition: false,   // Are we in edit mode?
  editValue: null,  // The currently edited but not saved value
}

The proper combinations of the properties will give us the 4 states we have identified above.

属性的正确组合将为我们提供上面确定的4种状态。

The problem is that there are actually 2⁵ = 32 possible combinations for the state. This means there are 28 wrong ways to use the state properties.

问题是该状态实际上有2⁵= 32种可能的组合。 这意味着有28种错误的方式使用状态属性。

One typical error on the component above is to not reset the error after a successful save. So the end user will save, get a “Something went wrong” error message, correct the error, save again and go to display mode. So far so good. Except when going to edit mode again … the error message is still there. True story. I’ve seen this done several times by inexperienced developers.

上面的组件的一个典型错误是成功保存后不重置该错误。 因此,最终用户将进行保存,得到“Something went wrong”出现“Something went wrong”错误消息,更正错误,再次保存并进入显示模式。 到目前为止,一切都很好。 除了再次进入编辑模式时,错误消息仍然存在。 真实的故事。 我已经看到经验不足的开发人员多次这样做。

Our component is as simple as it gets and yet it evinces a sad truth:

我们的组件非常简单,但却显示出一个可悲的事实:

Operating on raw state properties means the component robustness relies solely on the correct use of the properties meaning … for each developer modifying the code … through the whole project lifecycle.

对原始状态属性进行操作意味着组件的健壮性完全取决于属性的正确使用,这意味着…对于每个修改代码的开发人员…在整个项目生命周期中。

We all know how this ends!

我们都知道结局!

解决方案 (The solution)

Consider a different approach using “state machines”. The states would be:

考虑使用“状态机”的另一种方法。 状态将是:

state: {
  display: {
    processing: false,
    error: null,
    value: “Awesome”,
    edition: false,
    editValue: null,
  },
  saving: {
    processing: true,
    error: null,
    value: “Awesome”,
    edition: true, // Keep the edit view active until save is finished
    editValue: “Awesome Edit”, 
  },
  edit: {
    processing: false,
    error: null,
    value: “Awesome”,
    edition: true,
    editValue: “Awesome Editing”,
  },
  save_error: {
    processing: false,
    error: “Value should be at least 4 characters”,
    value: “Awesome”,
    edition: true, // Keep the edit box open
    editValue: “Awe”,
  }
}

This is more verbose than the first approach but it provides many benefits:

这比第一种方法更为冗长,但是它提供了许多好处:

  • One can see all the states of the component by just looking at the state machine. States have logical names and usage of each raw property is self-documented. New developers in the team will feel at home right away.

    只需查看状态机,即可看到组件的所有状态。 状态具有逻辑名称,并且每个原始属性的用法都是自记录的。 团队中的新开发人员将立即感到宾至如归。
  • The convention on how to extend the component is clear: create a new state and set the raw properties appropriately. No one in their right mind would dare to use raw setState() when there is a state machine implemented in the component.

    关于如何扩展组件的约定很明确:创建一个新状态并适当设置原始属性。 当组件中实现了状态机时,没有一个人会敢于使用raw setState()

  • The last but not the least, the handover process with the UI/UX team becomes as smooth as it can be. You need a visual design for each state of your machine, and maybe some animations for the transitions. That’s it. Clear and easily trackable.

    最后但并非最不重要的一点是,UI / UX团队的移交过程变得尽可能顺利。 您需要为计算机的每种状态进行视觉设计,并且可能需要一些动画来进行过渡。 而已。 清晰易懂。

A minimalistic working version of the example above would be:

上面示例的简约工作版本为:

import React, {Component, PropTypes} from 'react';

export default class InputStateMachine extends Component {
    constructor(props) {
        super(props);

        this.handleSubmit = this.handleSubmit.bind(this);
        this.goToState = this.goToState.bind(this);
        this.save = this.save.bind(this);

        this.state = {
            name: 'display',
            machine: this.generateState('display', props.initialValue)
        };
    }

    generateState(stateName, stateParam) {

        const previousState = this.state ? {...this.state.machine} : {};

        switch(stateName) {
            case 'display':
                return {
                    processing: false,
                    error: null,
                    value: stateParam || previousState.value,
                    editing: false,
                    editValue: null,
                };
            case 'saving':
                return {
                    processing: true,
                    error: null, // Reset any previous error
                    value: previousState.value,
                    editing: true, // Keep the edit view active until save is finished
                    editValue: previousState.editValue,
                };
            case 'edit':
                return {
                    processing: false,
                    error: null,
                    value: previousState.value,
                    editing: true,
                    editValue: stateParam,
                };
            case 'save_error':
                return {
                    processing: false,
                    error: stateParam,
                    value: previousState.value,
                    editing: true, // Keep the edit box open
                    editValue: previousState.editValue,
                };
            case 'loading': // Same as default
            default:
                return {
                    processing: true,
                    error: null,
                    value: null,
                    editing: false,
                    editValue: null,
                };
        }
    }

    goToState(stateName, stateParam)  {
        this.setState({
            name: stateName,
            machine: this.generateState(stateName, stateParam)
        });
    }

    handleSubmit(e) {
        this.goToState('edit', e.target.value);
    };

    save(valueToSave) {
        this.goToState('saving');

        // Simulate saving the data ...
        setTimeout(() => this.goToState('display', valueToSave), 2000);
    };

    render() {
        const {processing, error, value, editing, editValue} = this.state.machine;

        if(processing) {
            return <p>Processing ...</p>
        } else if(editing) {
            return (
                <div>
                    <input type="text" onChange={this.handleSubmit} value={editValue || value} />
                    {error && <p>Error: {error}</p>}
                    <button onClick={() => this.save(editValue)}>Save</button>
                </div>
            );
        } else {
            return (
                <div>
                    <p>{value}</p>
                    <button onClick={() => this.goToState('edit', value)}>Edit</button>
                </div>
            );
        }
    }
}

Usage is:

用法是:

<InputStateMachine initialValue="Hello" />

There is a bit of boilerplate code to write when using state machines:

使用状态机时,需要编写一些样板代码:

  • Create a utility method that sets the state name and content. Keep track of the current state name to ease debugging.

    创建一个设置状态名称和内容的实用程序方法。 跟踪当前状态名称以简化调试。
  • Keep the method that generates your state pure and use it to initialize your state in the constructor

    保持生成状态的方法纯净,并使用它在构造函数中初始化状态
  • Destructure this.state.machine instead of this.state in your render method

    this.state.machine而不是this.state this.state 在您的渲染方法中

  • State may need parameters which can be difficult to handle. As a rule of thumb, if your state generation requires more than 3 parameters then your component should not use the state machine pattern

    状态可能需要难以处理的参数。 根据经验,如果您的状态生成需要三个以上的参数,则您的组件不应使用状态机模式

Some libraries aim to solve this boilerplate issue but the overhead is so small that it does not really deserve a new dependency on your project.

一些库旨在解决此样板问题,但是开销如此之小,以至于它实际上不值得您的项目依赖。

结论 (Conclusion)

The state machine pattern is a good way to improve your UI components readability and development process from visual design to maintenance.

从视觉设计到维护,状态机模式是提高UI组件的可读性和开发过程的好方法。

Careful though! Do not go all in and apply this to all the components you have! Your app needs to remain flexible and handle emergent complexities. The number of states can quickly explode for higher level components and state machines are of no benefit in that case.

小心点! 不要全力以赴,并将其应用于您拥有的所有组件! 您的应用程序需要保持灵活性并应对紧急情况。 对于更高级别的组件,状态数量会Swift爆炸,而在这种情况下,状态机毫无用处。

Do use the pattern on your library of standard/base components though! This is the part of the application that will live the longest. Eventually, each developer in the team will touch it and benefit from the guidance and robustness provided by the state machine.

不过,请务必在您的标准/基本组件库中使用该模式! 这是应用程序中寿命最长的部分。 最终,团队中的每个开发人员都会接触它,并从状态机提供的指导和强大功能中受益。

Thanks for reading!

谢谢阅读!

翻译自: https://www.freecodecamp.org/news/boost-your-react-with-state-machines-1e9641b0aa43/

react中的状态机

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐