Volodymyr Humeniuk
Volodymyr Humeniuk

Reputation: 3791

Toggle focus between inputs from another component

I have next code:

<Wrapper>
   <InputsGroup />
   <Controls />
</Wrapper>

In InputsGroup component I have a list of inputs:

// InputsGroup 
<>
   <input ... />
   <input ... />
   <input ... />
   ...
</>

In Controls component I have two buttons: Next and Previously.

// Controls

<>
   <button> Prev </button>
   <button> Next </button>
</>

The idea, is I need to toggle focus between inputs when click 'Next' or 'Prev' button.

What is the best way to do it, and how can I manage the focus of inputs?

This is how it's looks like:
enter image description here

Upvotes: 1

Views: 1363

Answers (2)

knightburton
knightburton

Reputation: 1164

I would use the local state to track the focused input field.

In the outer componenent:

state = {
  focused: 'input_name' || defaultValue
};

handleFocusChange = focused => {
  // Use your own logic to change the focus
  this.setState({ focused });
};

render() {
  const { focused } = this.state;

  return (
    <Wrapper>
      <InputsGroup focused={focused} />
      <Controls onClick={this.handleFocusChange} />
    </Wrapper>
  );
}

Inside your InputsGroup component where you render your inputs:

const { focused } = this.props;

<>
  {/* .... */}
  <input
    {/* ... */}
    autoFocus={focused === inputName}
  />
</>

At the end in your Controls component:

const { onClick } = this.props;
<>
 <button onClick={() => onClick(/* you need your own logic here, too */)}> Prev </button>
 <button onClick={() => onClick(/* you need your own logic here, too */)}> Next </button>
</>

Usually I keep my input field options in a separated constant file, therefore the logic can be super easy to implement. You can use this to walk through the inputs with button click or render the input fields.

Example of a possible field map:

const fields = [
  { name: 'input_name_1', type: 'text', ... },
  { name: 'input_name_2', type: 'text', ... }
];

Upvotes: 3

Engineer
Engineer

Reputation: 1261

You can solve this using Refs concept, this is a working solution:-

class App extends React.Component{
  constructor(props) {
    super(props);
    this.state = {
      focus: 0
    };
    this.textInput0 = React.createRef();
    this.textInput1 = React.createRef();
    this.textInput2 = React.createRef();
  }
  componentDidMount() {
    this.textInput0.current.focus();
  }
  clickHandler = btnType => {
    let focus = this.state.focus;
    if (btnType === "prev") {
      if (focus !== 0) focus--;
    } else {
      if (focus !== 2) focus++;
    }

    this.focusInputField(focus);
    this.setState({ focus: focus });
  };
  focusInputField = id => {
    //console.log(id);
    switch (id) {
      case 0:
        this.textInput0.current.focus();
        break;
      case 1:
        this.textInput1.current.focus();
        break;
      case 2:
        this.textInput2.current.focus();
        break;
      default:
        this.textInput0.current.focus();
    }
  };
  render() {
    return (
      <React.Fragment>
        <input placeholder="textInput0" ref={this.textInput0} />
        <br />
        <input placeholder="textInput1" ref={this.textInput1} />
        <br />
        <input placeholder="textInput2" ref={this.textInput2} />
        <br />
        <button onClick={() => this.clickHandler("prev")}>Prev</button>
        <button style={{marginLeft: "20px"}} onClick={() => this.clickHandler("next")}>Next</button>
      </React.Fragment>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'></div>

Upvotes: 1

Related Questions