确定州

时间:2017-01-21 14:26:39

标签: reactjs redux

由于ReactJS(和其他通量实现,如redux)建议:

https://facebook.github.io/react/docs/thinking-in-react.html#step-3-identify-the-minimal-but-complete-representation-of-ui-state

我们不应该保持一个可以从其他状态计算的状态(我们称之为派生数据),否则当我们设置状态时,我们必须设置另一个状态。

但请考虑以下示例:

  

我的页面中有两种状态

     
      
  1. 红绿灯的颜色:灯=红色/绿色
  2.   
  3. 是否有人来过马路: people = true (有人)/ false (没有人)
  4.         

    众所周知,当红绿灯处于红灯时,人们无法过马路。

         

    因此,我们有3个正常的组合状态:

         
        
    1. light = green,people = true
    2.   
    3. light = green,people = false
    4.   
    5. light = red,people = false
    6.         

      另一个状态 light = red且people = true 无效。

           

      light = red 时,我们可以计算 people = false ,在这种情况下, light的派生数据。但是当 light = green 时我们不能。

问题是当我设置 light 值时,我必须设置 people 值,否则可能导致无效状态。

那么如何解决这个问题呢?如何设计我的州?

是否有任何图书馆可以帮助管理它们。首先,我可以定义我的状态之间的依赖关系。库是对它们一致性的响应。当我设置一个与他人冲突的状态值时,其他值将被修改为正确的值。

我写了一个简单的例子来展示我想要的东西,有没有像这样的图书馆?

class ComplexState {
    constructor(config, initialState){
        this.config = config;
        this.state = initialState;

        this._checkAll();
    }

    setState(key, value){
        var config = this.config[key];
        if(config && config.type === ComplexState.Types.Enum){
            //Set Enum data type
            if(Array.isArray(config.definitionField) && config.definitionField.indexOf(value) !== -1 || 
                config.definitionField[value] && typeof config.definitionField[value].checker === 'function' && config.definitionField[value].checker(this.state)){
                //Set the value
                this.state[key] = value;
                //If there is a conflict, modify other values
                Object.keys(this.state).forEach(_key => {
                    if(key === _key){//Self
                        return;
                    }
                    var _value = this.state[_key];
                    var _config = this.config[_key];
                    if(!Array.isArray(_config.definitionField) && _config.definitionField[_value] && typeof _config.definitionField[_value].checker === 'function' && !_config.definitionField[_value].checker(this.state)){
                        //Conflict, adapt to correct value
                        this.state[_key] = _config.definitionField[_value].adapter(this.state);
                    }
                })

                //Check for conflicts
                this._checkAll();
            }else{
                //Invalid value
            }
        }
    }

    getState(){
        return Object.assign({}, this.state);
    }

    //Check for conflicts
    _checkAll(){
        Object.keys(this.state).forEach(_key => {
            var _value = this.state[_key];
            var _config = this.config[_key];
            if(!Array.isArray(_config.definitionField) && _config.definitionField[_value] && typeof _config.definitionField[_value].checker === 'function' && !_config.definitionField[_value].checker(this.state)){
                //Conflict
                throw new Error('invalid state' + JSON.stringify(this.state));
            }
        })
    }
}

ComplexState.Types = {
    Enum : 0
}

使用它,

var myStates = new ComplexState({
    light : {
        type : ComplexState.Types.Enum,
        definitionField : ['red', 'green']
    },
    people : {
        type : ComplexState.Types.Enum,
        definitionField : {
            'false' : null,
            'true' : {
                checker : function(state){
                    return state.light !== 'red';
                },
                adapter : function(state){
                    if(state.light === 'red'){
                        return 'false';
                    }
                    return state.people;
                }
            }
        }
    }
}, {
    light : 'Green',
    people : 'false'
})
console.log(myStates.getState());//{light: "Green", people: "false"}
myStates.setState('people', 'true');
console.log(myStates.getState());//{light: "Green", people: "true"}

//when I set light value to red, the people value is automatically set to false
myStates.setState('light', 'red');
console.log(myStates.getState());//{light: "red", people: "false"}

2 个答案:

答案 0 :(得分:0)

如果你只需要在设置另一个状态时需要输入值,你可以这样做:

this.setState({
  light: someNewColor,
  people: someNewColor === red ? false : this.state.people
});

如果someNewColor为红色,则会自动将人设置为false。否则它将保持之前的设定值。

编辑:

如果你想让这个更干净,你必须让人和光完全独立。例如:

this.setState({
  light: someNewColor
})

目前这不起作用,因为你无法只用光来计算人。

答案 1 :(得分:0)

我开发了一个库来解决这个问题。 https://github.com/hanjunspirit/cascade.js