简体   繁体   中英

How to set react state with new props immediately after calling `dispatch` via react-redux connect?

I'm new to React and Redux and I'm trying to build a website. I'm using Material-UI components. The AppSearchBarInput is an input in the header where users can search for an id (referred to as appId in the code). If appId is found in the database then I want to redirect to another page ( this.props.history.push('/app/appInfo'); ). If the input is blank then I display a snackbar ( AppNotFound ) with warning message as well as when the input doesn't exist in the database.

In the onKeyDown method I use connect to dispatch an action which checks if the id exists in the database ( dispatch is called from getAppInfo using redux-thunk ). Then I need to immediately get the new Redux state via props in order to decide whether the id was found or not and depending on this I set snackBarOpen local state property to true or false.

The problem is that I can't get the updated props within the same method when calling dispatch. I could use componentDidUpdate but then I can't update local store from it. Is there some workaround for this situation either using some react lifecycle method or redux trick?

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;

    if (e.keyCode === constants.ENTER_KEY) {
      if (appId.trim() !== '') {
        this.props.getAppInfo({ appId });
        this.setState({
          appId: ''
        });

        const { found } = this.props.app; // ! problem here !
        if (found) {
          this.props.history.push('/app/appInfo');
        } else {
          this.setState({
            snackBarOpen: true
          });
        }
      } else {
        this.setState({
          snackBarOpen: true
        });
      }
    }
  }

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

  render() {

    const { classes } = this.props;
    const { appId, snackBarOpen } = this.state;
    const { found } = this.props.app;
    let message = '';
    if (!found) {
      message = appId === '' ? constants.MESSAGES.APP_BLANK() : constants.MESSAGES.APP_NOT_FOUND(appId);
    }

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

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

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

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

From the look of your code you have no dispatch method. If data was really fetched, then you're not getting updates because withRouter actually blocks shouldComponentUpdate. If you want updated state, wrap the connect in withRouter .

You should also have a mapDispatchToProps function. Like this:

const mapDispatchToProps = dispatch => ({
  makeDispatch: () => dispatch({ getAppInfo })
})

So, instead of:

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

You should have:

const AppSearchBarWithStyles = withStyles(AppSearchBarInputStyles)(AppSearchBarInput);
AppSearchBarWithStylesWithConnect = connect(mapStateToProps, mapDispatchToProps)(AppSearchBarWithStyles);
export default withRouter(AppSearchBarWithStylesWithConnect);

Please see this: https://reacttraining.com/react-router/core/api/withRouter

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM