【问题标题】:How to manage UI feedback?如何管理 UI 反馈?
【发布时间】:2015-07-24 17:48:43
【问题描述】:

是否存在用于管理用户与各个组件的交互的既定模式,例如显示加载器微调器、在保存/加载表单时禁用输入字段等?

我发现自己在商店中执行以下操作,以使组件在某种程度上与任何隐含状态脱钩:

function CampaignStore() {
    EventEmitter.call(this);

    AppDispatcher.register(payload => {
        switch (payload.type) {
            // [#1] ---------------v  (main action)
            case CampaignContants.SAVE:
                // [#2] ------------------------v  (prepare for the main action)
                this.emit(CampaignContants.WILL_SAVE);

                const data = payload.data;
                if (data.id) {
                    // [#3] ---v  (perform main action in store)
                    updateCampaign(payload.data).then(_ => {
                       // [#4] ------------------------v  (after main action)
                       this.emit(CampaignContants.DID_SAVE, 0)
                    });
                } else {
                    insertCampaign(payload.data).then(campaignId => this.emit(CampaignContants.DID_SAVE, campaignId));
                }
                break;
            // ...
        }
    }
}

基本上,我只是触发一个事件,表示某个动作即将发生,然后我执行该动作(进行 API 调用等),然后在该动作完成时发出另一个事件。

在组件内部,我可以订阅WILL_<action> 事件,渲染所有微调器等,然后在触发DID_<action> 时清除屏幕。虽然这似乎可行,但它确实感觉非常样板和重复,以及超级混乱(太多的状态只能根据动作的位置来调整 UI(在WILL_<action> 和 *DID_<action> 之间)。

// some component
var MyComponent = React.createClass({
   getInitialState: function () {
       return {
           items: [],
           loading: false,
           saving: false,
           checkingPasswordStrength: fase,
           // ...
       };
   },
   render: function(){
      return (
          <div>
             {this.state.loading && (
                 <p>Loading...</p>
             )}

             {!this.state.loading && (
                 // Display component in not-loading state
             )}
          </div>
      );
   }
});

【问题讨论】:

    标签: reactjs design-patterns imperative-programming


    【解决方案1】:

    我认为您最好使用componentWillMountcomponentDidMountcomponentWillUpdatecomponentWillUnmount 等生命周期方法。使用这些方法,您可以检查上一个/当前/下一个道具/状态(取决于方法)并做出响应。这样一来,您的 store 只处理您的状态,并且您的组件变得更加纯净。

    【讨论】:

      【解决方案2】:

      我们在这里找到了一个简单的加载容器组件。 所以是这样的:

      const LoadingContainer = React.createClass({
          getDefaultProps: function() {
            return {isLoadedCheck:(res) => res.data!=null }
          },
          getInitialState: function() {
            return {isLoaded:false, errors:[]}
          },
          componentDidMount: function() {
            if(this.props.initialLoad) { this.props.initialLoad(); }
            if(this.props.changeListener) { this.props.changeListener(this.onChange); }
          },
          onChange: function() {
            let res = this.props.loadData();
            this.setState({errors: res.errors, isLoaded: this.props.isLoadedCheck(res)});
          },
          render: function() {
            if(!this.state.isLoaded) {
              let errors = this.state.errors && (<div>{this.state.errors.length} errors</div>)
              return (<div>{errors}<LoadingGraphic /> </div>)
            }
            return <div>{this.props.children}</div>
          }
        });
      
        const Wrapper = React.createClass({
          getDefaultProps: function() {
            return {id:23}
          },
          render: function() {
            let initialLoad = () => someActionCreator.getData(this.props.id);
            let loadData = () => someStore.getData(this.props.id);
            let changeListener = (fn) => someStore.onChange(fn);
            return (<div><LoadingContainer initialLoad={initialLoad}
              changeListener={changeListener}
              loadData={loadData}
              isLoadedCheck={(res) => res.someData != null}><SomeComponent id={this.props.id} /></LoadingContainer></div>)
          }
        });
      

      虽然它添加了另一个无状态包装器,但它提供了一种干净的方式来确保您的组件不只是在挂载时加载,并且提供了一个显示 api 反馈等的公共位置。 随着 react 14 的出现,这些纯粹的无状态包装器得到了一点推动,性能改进即将到来,所以我们发现它可以很好地扩展

      【讨论】:

        【解决方案3】:

        这种模式将帮助您让您的各个组件管理用户交互

        var MyComponent = React.createClass({
        getInitialState: function() {
          return {
            item: [],
            loading: true,
          };
        },
        componentDidMount: function() {
          //Make your API calls here
          var self = this;
          $.ajax({
            method: 'GET',
            url: 'http://jsonplaceholder.typicode.com/posts/1',
            success: function(data) {
              if (self.isMounted()) {
                self.setState({
                  item: data,
                  loading: false
                });
              }
            }
          });
        },
        render: function() {
          var componentContent = null;
          if (this.state.loading) {
            componentContent = (<div className="loader"></div>);
          } else {
            componentContent = (
              <div>
                <h4>{this.state.item.title}</h4>
                <p>{this.state.item.body}</p>
              </div>
            );
          }
          return componentContent;
        }});
        

        【讨论】:

        • 这与我发布的内容有何不同?
        • 您发布的内容(这是一个好方法)在大多数情况下都可以正常工作,但有可能首先执行相关操作(如果有)并完全改变您的 ui 操作.在大规模上,回流派上用场,但你需要小心使用你的商店。另一方面,不在商店中解耦特定于组件的逻辑将真正帮助您克服这些问题
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-11
        • 2011-02-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多