简体   繁体   中英

Reset an input field after submission

i have some issues dealing with a simple case in my redux-react app: i want to reset an input text after an asynchronous operation ignited by a button.

Let's say we have an input text in which you put a text and this is passed through a onClick event to a dispatch action. This action contacts a server and after the server response i want to reset the input field.

I've implemented a number of solutions (i'm using redux thunk) to this problem but i'm not sure if they are hacky ways to solve it, let me show you:

1) Presentational component (the input field) implements a reset method that is passed as a value to the onClick method.

export default React.createClass({

  reset: function () {
    this.setState({searchText: ''})
  },

  getInitialState: function () {
    return {
      searchText: ''
    }
  },

  render: function () {
    return (
        <div>
          <TextField
            value={this.state.searchText}
            onChange={e => this.setState({ searchText: e.target.value })}
          />
          <RaisedButton
            onClick={this.props.startSearch.bind(null,
              this.state.searchText,
              this.reset)} // ===> HERE THE RESET FUNCTION IS PASSED
          />
        </div>
    )
  }
})

The container dispatches the action and then calls the reset method.

const mapDispatchToProps = (dispatch) => {
  return {
    startSearch: (searchText, reset) => {
      dispatch(actions.startSearch(searchText))
      .then(() => reset())
    }
  }
}

2) Using ref ( https://facebook.github.io/react/docs/refs-and-the-dom.html )

The container gets a reference to its child and calls reset through it

const SearchUserContainer = React.createClass({

  startSearch: (searchText) => {
    dispatch(actions.startSearch(searchText))
    .then(() => this.child.reset())
  },

  render: function () {
    return (
      <SearchUser {...this.props} ref={(child) => { this.child = child; }}/>
    )
  }
})

3) The Redux Way.

searchText is managed by the store thus the action dispatched triggers a resolver that reset the searchText value, the container updates its child and we are done, well… almost: the presentational component is a controlled component ( https://facebook.github.io/react/docs/forms.html#controlled-components ) that means it manages the input text as an internal state, i think we have to find a way to make the two 'state managers' coexist.

I wrote this code to manage the internal state and the state coming from redux, in few words the presentational gets the initial value from redux, then updates it in the onChange event and it's ready to receive updates from redux thanks to componentWillReceiveProps .

export default React.createClass({

  getInitialState: function () {
    return {
      searchText: this.props.searchText ==> REDUX
    }
  },

  componentWillReceiveProps: function (nextProps) {
    this.setState({
      searchText: nextProps.searchText ==> REDUX
    })
  },

  render: function () {
    return (
        <div>
          <TextField
            value={this.state.searchText}
            onChange={e => this.setState({ searchText: e.target.value })}
          />
          <RaisedButton
            onClick={this.props.startSearch.bind(null, this.state.searchText)}
          />
        </div>
    )
  }
})

4) Redux-Form To complete the picture i link the redux-form options to do that http://redux-form.com/6.5.0/docs/faq/HowToClear.md/

What do you think about those ideas? Thanks.

Go the Redux way, except go all the way: remove the internal state from your component completely and let Redux handle it (might as well make your component a pure-functional component too):

Component:

import { connect } from 'redux';
import { actions } from 'actionCreators';

const ControlledInputComponent = (props) => {
  return (
    <div>
      <TextField
        value={this.props.searchText}
        onChange={e => this.props.setSearchText(e.target.value)}
      />
      <RaisedButton
        onClick={this.props.startSearch}
      />
    </div>
  );
};

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

const mapDispatchToProps = (dispatch) => {
  return {
    setSearchText: (txt) => { dispatch(actions.setSearchText(txt)); },
    startSearch: () => { dispatch(actions.search()); }
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(ControlledInputComponent);  

Action creator:

export const actions = {
  setSearchText: (txt) => ({ type: 'setText', data: txt }),

  //here's where the thunk comes in
  //make sure you have redux-thunk and add it as a middleware when setting up the store, etc.

  search: () => {
    return (dispatch) => {
      //use fetch or whatever to run your search (this is a simplified example)
      fetch(/* your url here */).then(() => {
        //presumably a success condition

        //handle the search results appropriately...

        //dispatch again to reset the search text
        dispatch(actions.setSearchText(null);
      });
    };
  }
};

Reducer:

const reducer = (state = { searchText: null }, action) => {
  if (!action || !action.type) return state;
  switch (action.type) {

    //you should really define 'setText' as a constant somewhere
    //so you can import it and not have to worry about typos later
    case 'setText':
      return Object.assign({}, state, { searchText: action.data });

    default:
      return state;
  }
};

export default reducer;

Hopefully that helps. Good luck!

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