如何使用 thunks 根据 redux 状态有条件地显示小吃店?

How to display a snackbar conditionally based on redux state using thunks?

我正在用 React 编写一个显示移动应用程序信息的网站,我正在使用 redux 来存储有关当前应用程序的信息。我有一个位于 header 的输入文本字段,用户可以在其中输入一些应用程序 ID,如果它是一个有效的 ID,他们将被重定向到另一个页面,如果该 ID 无效,则 snackbar 将显示适当的消息,如果用户只是点击回车,小吃店也将显示适当的消息。 AppNotFound 是包装 snackbar 的组件。

我在 onKeyDown 方法 (getAppInfo) 中使用 redux thunks 来分派一个动作来检查应用程序 ID 是否有效(它是一个异步函数)。理想情况下,我想从已经在 onKeyDown 方法中的 redux 中获得结果。但是因为动作是异步调度的,所以我不能。

所以我想让 render 根据 found 属性 的值(无论是否找到应用程序)来显示一个 snackbar。所以起初 found 会是 undefined 因为异步调度不会在 render 中返回结果但是 found 会变成 truefalse 然后我们可以显示小吃店。渲染将自动第二次调用,因为道具已更改。但这并没有发生。

实现我想要的模式的正确方法是什么?我不想使用 componentWillReceiveProps,因为它已被弃用。

这是我的代码:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import InputBase from '@material-ui/core/InputBase';
import { withStyles } from '@material-ui/core/styles';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { getAppInfo } from '../../actions/appActions.js';
import constants from '../../constants.js';

import { AppSearchBarInputStyles } from '../styles/Material-UI/muiStyles.js';
import AppNotFound from './AppNotFound.js';

import * as log from 'loglevel';
log.setLevel("debug")


class AppSearchBarInput extends Component {
  state = {
    appId: '',
    snackBarOpen: false
  }

  onChange = e => {
    this.setState({ appId: e.target.value });
  }

  onKeyDown = e => {
    const { appId } = this.state;
    const { found } = this.props.app;

    if (e.keyCode === constants.ENTER_KEY) {

      this.props.getAppInfo({ appId });
      if (found) {
        this.props.history.push('/moreInfo');
      } else {
        this.setState({
          snackBarOpen: true
        });
      }

      this.setState({
        appId: ''
      });
    }
  }

  handleCloseSnackBar = () => {
    this.setState({
      snackBarOpen: false
    });
  }

  render() {

    const { classes, app } = this.props;
    const { snackBarOpen } = this.state;
    const { found, appId } = app;
    let message = '';

    if (!found) {
      message = appId === '' ? constants.MESSAGES.APP_BLANK() : constants.MESSAGES.APP_NOT_FOUND(appId);
    }

    let displayWhenFoundIsUndefined = null;

    if (found === undefined) {
      displayWhenFoundIsUndefined = <div>Loading...</div>;
    } else {
      displayWhenFoundIsUndefined = <AppNotFound message={message}
                   open={!found}
                   onClose={this.handleCloseSnackBar}/>;
    }
    return (
      <div>
        <InputBase
          placeholder="Search…"
          classes={{
            root: classes.inputRoot,
            input: classes.inputInput,
          }}
          onChange={this.onChange}
          onKeyDown={this.onKeyDown}
          value={this.state.appId} />
        {displayWhenFoundIsUndefined}
      </div>
    )
  }
}

AppSearchBarInput.propTypes = {
  classes: PropTypes.object.isRequired
}

const mapStateToProps = state => ({
  app: state.app.app
});

const AppSearchBarWithStyles = withStyles(AppSearchBarInputStyles)(AppSearchBarInput);
const AppSearchBarWithStylesConnected = connect(mapStateToProps, { getAppInfo })(AppSearchBarWithStyles);
export default withRouter(AppSearchBarWithStylesConnected);

既然没有人回答这个问题,我将提供我最近得到的解决方案。

问题本质上是 AppSearchBarInput 的显示主要取决于是否找到该应用程序。此操作必须是异步的,因为信息来自 Web API。因此,我正在使用 redux-thunks 并在 props 中接收移动应用程序信息。然而 snackbarOpen 属性 处于一个问题状态,因为小吃店依赖于状态 属性,而状态本身依赖于异步接收的道具。

解决困境的方法是snackbarOpen也移动到道具上。因此,如果未找到该应用程序,那么现在应将 snackbarOpen 设置为 true 的操作直接从 getAppInfo thunk 发送,如果应用程序为空,则应从 onKeyDown 发送。另一方面,将 snackbarOpen 设置为 false 的操作应该从 handleCloseSnackBar 发送。