【问题标题】:Loading screen using React + Redux使用 React + Redux 加载屏幕
【发布时间】:2020-04-29 15:11:42
【问题描述】:

现在我有一个使用 redux 的 react 应用程序。我创建了一个组件,它作为一个表单来创建客户。像这样:

import { createCustomer } from "../../actions";

class CustomerCreate extends React.Component {

  state = {
    submitting: false
  }

  componentWillReceiveProps(nextProps) {
    this.setState({ submitting: false });
  }

  submitData = (data) => {
    this.setState({ submitting: true });
    this.props.createCustomer(data);
  };

  render() {
    return (
      <React.Fragment>
        {this.state.submitting ? <LoadingScreen /> : null}
        {this.props.error ? <ErrorMessage message={this.props.error}/> : null}
        <FormContainer
        ...
        />
      </React.Fragment>
    );
  }
}

const mapStateToProps = ({ customerData }) => {
  return {
    error: customerData.error
  };
};

export default connect(mapStateToProps, { createCustomer })(
  CustomerCreate
);

这“很好”。基本上 componentWillReceiveProps 将在每次发生某些事情时被调用(错误,好的),所以我会知道何时删除 LoadingScreen。问题是 componentWillReceiveProps 正在引发警告(说不应该使用),并且根据 redux 文档,这被标记为反模式。

我知道一种解决方案可能不是在这里使用 redux。使用 axions 调用更改 submitData 并将 setState 放在那里。但我这样使用它是因为错误消息之一可能是:SESSION EXPIREDINVALID JWT TOKEN 所以我需要将网页重新发送到登录屏幕。

我能想到的另一种方法是在操作中调用调度以设置 isSubmitting 状态。像这样的:

export const createCustomer = (data) => {
  /* IS THIS VALID? */
  dispatch({ type: CUSTOMER_SUBMITTING });
  return async function (dispatch) {
    try {
      const response = await services.post("/api/customer", data, buildHeader());
      dispatch({ type: CUSTOMER_CREATED, payload: response.data });
    } catch (err) {
      dispatch({ type: CUSTOMER_ERROR, payload: err });
    }
  };
};

我将使用 props.isSubmitting,而不是使用 state.isSubmitting。但我不确定这样调用两个dispath是否有效/正确。

所以,我的问题是:如何实现在提交时设置为 true 并在 axios 调用完成时设置为 false 的状态/道具(由于某些错误或刚刚好)。

【问题讨论】:

  • 我建议你看看redux-saga
  • componentDidUpdate(prevState, prevProps, snapshot) 不能为您工作?当来自 redux 的 prop 更新时,您可以将其与之前的 prop 值进行比较,并将您的提交状态设置为 false。
  • 根据我的经验,我通常会应用您提到的第二种解决方案-将isSubmitting添加到redux。我相信在 redux 中处理所有请求状态(isFecting、isError、data...)更容易管理。大量的 redux 包可以帮助你用更少的代码做到这一点。

标签: reactjs react-redux


【解决方案1】:

使用componentDidUpdate 生命周期函数。在redux中添加一个表单处理成功的action和state,并映射到props。

import { createCustomer } from "../../actions";

class CustomerCreate extends React.Component {

  state = {
    submitting: false
  }

  componentDidUpdate(prevState, prevProps) {
    // If a new error comes in and was previously "falsey" set submitting false
    if (prevProps.error !== this.props.error && this.props.error) {
      this.setState({ submitting: false });
    }
    // Similar check for form success
  }

  submitData = (data) => {
    this.setState({ submitting: true });
    this.props.createCustomer(data);
  };

  render() {
    return (
      <React.Fragment>
        {this.state.submitting ? <LoadingScreen /> : null}
        {this.props.error ? <ErrorMessage message={this.props.error}/> : null}
        <FormContainer
        ...
        />
      </React.Fragment>
    );
  }
}

const mapStateToProps = ({ customerData }) => {
  return {
    error: customerData.error,
    success: /* some success value from redux */
  };
};

export default connect(mapStateToProps, { createCustomer })(
  CustomerCreate
);

或者作为一个功能组件,使用状态和效果钩子:

import { createCustomer } from "../../actions";

const CustomerCreate ({ createCustomer, error, success }) => {

  const [submitting, setSubmitting] = useState(false);

  useEffect(() => {
    if (error || success) setSubmitting(false);
  }, [error, success]);

  submitData = (data) => {
    setSubmitting(true);
    createCustomer(data);
  };

  return (
    <React.Fragment>
      {submitting ? <LoadingScreen /> : null}
      {error ? <ErrorMessage message={error}/> : null}
      <FormContainer
        ...
      />
    </React.Fragment>
  );
}

const mapStateToProps = ({ customerData }) => ({
  error: customerData.error,
  success: /* some success value from redux */
});

export default connect(mapStateToProps, { createCustomer })(
  CustomerCreate
);

【讨论】:

  • 问题是在render之后调用了componentDidUpdate,所以在那里调用setState会调用另一个render。这可能是性能问题吗?
  • 不是,不是。我说“不是真的”,因为对于大多数用例不会有性能问题,除非你的代码做的不是很聪明,应用反应反模式,或者调用超重或高延迟的函数。你试过了吗?通常会选择简单直接的解决方案,直到您真正注意到性能问题。我会说渲染一两次额外时间的组件不会属于性能问题的范畴。
【解决方案2】:

我使用这篇博文进行了一些更改,将加载状态与实际操作分开:

https://medium.com/stashaway-engineering/react-redux-tips-better-way-to-handle-loading-flags-in-your-reducers-afda42a804c6

我这样创建了一个 loadingReducer:

const INITIAL_STATE = {
}

const loadingReducer = (state = INITIAL_STATE, action) => {
    const { type } = action;
    const matches = /(.*)_(DELETE|REQUEST|SUCCESS|FAILURE)/.exec(type);

    // not a *_REQUEST / *_SUCCESS /  *_FAILURE actions, so we ignore them
    if (!matches) return state;
    const [, requestName, requestState] = matches;
    return {
        ...state,
        // Store whether a request is happening at the moment or not
        // e.g. will be true when receiving GET_TODOS_REQUEST
        //      and false when receiving GET_TODOS_SUCCESS / GET_TODOS_FAILURE
        [requestName]: requestState === 'REQUEST',
        [requestName + '_DELETE']: requestState === 'DELETE',
    };
};

export default loadingReducer;

然后创建函数 loadingSelector:

import _ from 'lodash';

export const createLoadingSelector = (actions) => (state) => {
    // returns true only when all actions is not loading
    return _(actions)
        .some((action) => _.get(state, action));
};

然后根据那个reducer创建了一个改变状态的组件:

class LoadingScreen extends React.Component {

    render() {
        if (!this.props.isLoading)
            return null;
        return (
            <Backdrop
                className={this.props.classes.backdrop}
                open={true}
            >
                <CircularProgress color="inherit" />
            </Backdrop>
        )
    }


}

const loadingListSelector = createLoadingSelector(['CUSTOMERS']);

const mapStateToProps = ({ loading }) => {
    return {
        isLoading: loadingListSelector(loading)
    };
};

【讨论】:

    猜你喜欢
    • 2019-05-20
    • 1970-01-01
    • 1970-01-01
    • 2021-07-21
    • 1970-01-01
    • 2023-04-11
    • 1970-01-01
    • 2018-01-22
    • 2019-09-28
    相关资源
    最近更新 更多