How to change input value in redux

16,912

There are uncontrolled(not set value) and controlled(set value) input in reactjs.

controlled not allow user input, but uncontrolled does.

Solution:

  1. Need use uncontrolled input(no value attribute).
  2. Select input element and set the value when currentPath change.

Bad way:

code:

export default class PathForm extends Component {
  changeCurrentPath(path) {
    const pathInput = document.querySelector('.current-path-input')
    if (pathInput){
      pathInput.value = path
      this.lastPath = path
    }
  }

  render() {

    const { currentPath,  handleSubmit } = this.props;
    console.log('PathFormPathFormPathForm', this.props)

    this.changeCurrentPath(currentPath)

    return (
      <div className="path-box">
        <form onSubmit={handleSubmit}>
          <div>
            <input type="text" className="current-path-input" placeholder="input path" />
          </div>
          <button className="go-btn" type="submit">Go</button>
        </form>
      </div>
    );
  }
}

Good way:

use componentWillReceiveProps to set props and rel to select element

1.use form submit

export default class PathForm extends Component {

  constructor(props) {
    super(props)
    // can not find `this` if not bind
    this.handleSubmit = this.handleSubmit.bind(this)
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.currentPath !== this.props.currentPath) {
      this.setInputValue(nextProps.currentPath)
    }
  }

  getInputValue() {
    return this.refs.pathInput.value
  }

  setInputValue(val) {
    this.refs.pathInput.value = val
  }

  handleSubmit(e){
    e.preventDefault()
    this.props.handleSubmit(this.getInputValue())
  }

  render() {

    return (
      <div className="path-box">
        <form onSubmit={this.handleSubmit}>
          <input className="current-path-input"
          defaultValue={this.props.currentPath}
          ref="pathInput" />
          <button className="waves-effect waves-light btn"  type="submit">Go</button>
        </form>
      </div>
    );
  }
}

2.use button click

export default class PathForm extends Component {

  constructor(props) {
    super(props)
    // can not find `this` if not bind
    this.handleGoClick = this.handleGoClick.bind(this)
    this.handleKeyUp = this.handleKeyUp.bind(this)
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.currentPath !== this.props.currentPath) {
      this.setInputValue(nextProps.currentPath)
    }
  }

  getInputValue() {
    return this.refs.pathInput.value
  }

  setInputValue(val) {
    this.refs.pathInput.value = val
  }

  handleKeyUp(e) {
    if (e.keyCode === 13) {
      this.handleGoClick()
    }
  }

  handleGoClick(e) {
    e.preventDefault()
    this.props.handleSubmit(this.getInputValue())
  }

  render() {

    return (
      <div className="path-box">
        <form >
          <input className="current-path-input"
          defaultValue={this.props.currentPath}
          onKeyUp={this.handleKeyUp}
          ref="pathInput" />
          <button className="waves-effect waves-light btn"  type="submit" onClick={this.handleGoClick}>Go</button>
        </form>
      </div>
    );
  }
}
Share:
16,912
Mithril
Author by

Mithril

I just want to get the badge....

Updated on June 12, 2022

Comments

  • Mithril
    Mithril almost 2 years

    I am making a file manager app based on react-redux, and I meet problem with input.

    For example, my code:

    PathForm.js:

    export default class PathForm extends Component {
    
      render() {
    
        const { currentPath,  handleSubmit } = this.props;
        console.log('PathFormPathFormPathForm', this.props)
    
        return (
          <div className="path-box">
            <form onSubmit={handleSubmit}>
              <div>
                <input type="text" className="current-path-input" placeholder="input path"  value={currentPath} />
              </div>
              <button className="go-btn" type="submit">Go</button>
            </form>
          </div>
        );
      }
    }
    

    Explorer.js:

    class Explorer extends Component {
    
      goPath(e) {
        e.preventDefault()
        // fake function here, because I have to solve the input problem first
        console.log('PathForm goPath:',this.props)
        let {targetPath , actions} = this.props
        swal(targetPath)
      }
    
      render() {
        const { node, currentPath , actions} = this.props
        console.log('Explorer.render:',this.props)
    
        return (
          <div className='explorer-container'>
            <PathForm currentPath={currentPath} handleSubmit={this.goPath.bind(this)}/>
            <FileListOperator />
            <FileListView fileList={node && node.childNodes} actions ={actions} />
          </div>
        );
      }
    }
    
    
    function mapStateToProps(state, ownProps) {
      return {
        node: state.tree[state.tree.currentPath],
        currentPath: state.tree.currentPath
      };
    }
    
    function mapDispatchToProps(dispatch) {
      console.log('mapDispatchToProps')
      return {
        actions: bindActionCreators(NodeActions, dispatch)
      };
    }
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(Explorer);
    

    Feature I want:

    I have a PathForm, it need show path from two way:

    1. user click a file path from left tree view, Explorer get this path as currentPath, then pass to PathForm, and show currentPath in input

    2. user directly type a path to the PathForm's input, PathForm call handleSubmit(Explorer's function) to change the currentPath

      Additional:I want to keep PathForm as a stateless component

    The problem:

    1. I'd like use PathForm as a stateless form, so I don't want connect it to store, but I need it change input by currentPath. But if I set value={currentPath}, user can not type anything else.
    2. change to <input type="text" onChange={this.changeValue} value={this.getValue()}/> allow user type string in this input, but can not use props currentPath passed by Explorer
    3. The only way I can imagine is connect this form to store which I don't want. I'd like Explorer to dispatch all actions and pass props.

    Tried with some package

    I found the input not act as my thought, so I tried the two popular package:

    1. redux-form

      It create a form need so much code, and official doc not say how to render this form with parent props, I try to pass props and handleSubmit to it, not work. After I see React + Redux - What's the best way to handle CRUD in a form component? and How to wire up redux-form bindings to the form's inputs I found I can't do that, it define some function overwrite mine, this behave is not good for me(I have to change the handlerSubmit function name, but it still not work), and it connect to the store. So I turn to formsy-react

    2. formsy-react

      It still need so much code, though it provide some mixin, but I still have to write a custom text input with changeValue function myself(changeValue is no need in most situation when writing normal html jquery app).Then I found the problem that PathForm can not use props currentPath passed by Explorer...

    Probably Worked solution(but I don't tend to use):

    connect PathForm to store, add another state inputPathValue for this input. Use inputPathValue interact with currentPath


    After above, I found use input/form is super in-convenient in react.... Does it mean I have to connect PathForm to stroe? Any other way to solve my problem?