Reputation: 469
I have a component that when the user hits submit (or enter) a new question is presented with a text input, the users enters in an answer and hits submits (this repeats until the final question is answered).
All works except that it's not focusing no matter if I add a ref or autofocus. I'm unsure what to do at this juncture and can't seem to get it to work no matter what I try.
Could I get some assistance on, when the user hits submit, it focuses on the next text input. I'm using styled components below in my code so the text input would be inputStyle
class NameForm extends React.Component {
constructor(props) {
super(props);
this.focus = this.focus.bind(this);
this.state = {
value1: '',
value2: '',
value3: '',
value4: '',
value5: '',
value6: '',
value7: '',
newValue: '',
submitted: false,
input1: 0,
input2: 0,
input3: 0,
input4: 0,
input5: 0,
input6: 0,
input7: 0,
display: 'block',
currentStep: 1,
whitebox: 'block'
};
this.handleFirstChange = event => this.handleChange(event, 'value1');
this.handleSecondChange = event => this.handleChange(event, 'value2');
this.handleThirdChange = event => this.handleChange(event, 'value3');
this.handleFourthChange = event => this.handleChange(event, 'value4');
this.handleFifthChange = event => this.handleChange(event, 'value5');
this.handleSixthChange = event => this.handleChange(event, 'value6');
this.handleSeventhChange = event => this.handleChange(event, 'value7');
this.handleSubmit = event => this._handleSubmit(event);
}
handleChange(event, type) {
let newState = {};
newState[type] = event.target.value;
this.setState(newState);
}
_handleSubmit(event) {
event.preventDefault();
if (this.state.currentStep > 6) {
this.setState({ visible: !this.state.visible });
this.setState({ display: 'none' });
this.setState({ whitebox: 'none' });
} else {
this.setState({ currentStep: this.state.currentStep + 1 });
}
}
inputHolderStyle(style, step) {
const displayProp = step === this.state.currentStep ? 'block' : 'none';
return {
// background: `url(${style}) no-repeat center center`,
// backgroundSize: 'cover',
// border: 'white 1px solid',
// background: '#00B5DE',
display: displayProp
};
}
focus() {
// Explicitly focus the text input using the raw DOM API
this.textInput.focus();
}
render() {
const divStyle = {
marginTop: '50px',
color: 'white',
top: '25px',
position: 'absolute',
width: '320px',
textAlign: 'center',
border: 'white 1px solid',
padding: '1em',
borderRadius: '3px',
display: this.state.whitebox
};
let question = null;
const show = this.state.visible;
if (show) {
question = (
<div>
<Crawler
properName1={this.state.value1}
noun1={this.state.value2}
properName2={this.state.value3}
properName3={this.state.value4}
noun2={this.state.value5}
personsName1={this.state.value6}
noun3={this.state.value7}
/>
</div>
);
}
return (
<MainContainer>
<div style={divStyle}>
<form
style={{ display: this.state.display }}
onSubmit={this.handleSubmit}
>
<InputHolder style={this.inputHolderStyle(ml1, 1)}>
<InputQuestion>1. Enter A Proper Noun</InputQuestion>
<label>
<InputStyle
name="input1"
type="text"
value={this.state.value1}
placeholder="Proper Noun"
onChange={this.handleFirstChange}
ref1={input => {
this.textInput = input;
}}
/>
<GrammarNerd>
Hint: Use words like Rebel, Hell's Angels, Vegan
</GrammarNerd>
</label>
</InputHolder>
<InputHolder style={this.inputHolderStyle(ml2, 2)}>
<InputQuestion>2. Enter A Location</InputQuestion>
<label>
<InputStyle
name="input2"
type="text"
ref={input => {
this.textInput = input;
}}
value={this.state.value2}
placeholder="Noun"
onChange={this.handleSecondChange}
ref2={input => {
this.textInput = input;
}}
/>
<GrammarNerd>
Hint: Use a word such as Base, Bunker, Foxhole, Bedroom
</GrammarNerd>
</label>
</InputHolder>
<InputHolder style={this.inputHolderStyle(ml3, 3)}>
<InputQuestion>
Enter A Proper Noun that Describes Evil
</InputQuestion>
<label>
<InputStyle
name="input3"
type="text"
placeholder="Enter a Proper Noun"
value={this.state.value3}
onChange={this.handleThirdChange}
ref3={input => {
this.textInput = input;
}}
/>
</label>
<GrammarNerd>
Hint: Use words like Empire, Ottoman, Mongols
</GrammarNerd>
</InputHolder>
<InputHolder style={this.inputHolderStyle(ml3, 4)}>
<InputQuestion>Describe Something Menacing</InputQuestion>
<label>
<InputStyle
name="input4"
type="text"
placeholder="Enter a Proper Name"
value={this.state.value4}
onChange={this.handleFourthChange}
ref4="theDiv"
/>
<GrammarNerd>
Hint: Freeze Ray, Mother of All Bombs, Leftover Fruitcake
</GrammarNerd>
</label>
</InputHolder>
<InputHolder style={this.inputHolderStyle(ml3, 5)}>
<InputQuestion>Describe a fortified area</InputQuestion>
<label>
<InputStyle
name="input5"
type="text"
placeholder="Enter a Noun"
value={this.state.value5}
onChange={this.handleFifthChange}
ref5={input => {
this.textInput = input;
}}
/>
<GrammarNerd>
Hint: Castle, Bunker, Planet, Safe Space
</GrammarNerd>
</label>
</InputHolder>
<InputHolder style={this.inputHolderStyle(ml3, 6)}>
<InputQuestion>A Woman's Name</InputQuestion>
<label>
<InputStyle
name="input6"
type="text"
placeholder="A Woman's Name"
value={this.state.value6}
onChange={this.handleSixthChange}
ref6={input => {
this.textInput = input;
}}
/>
</label>
<GrammarNerd>
Hint: Astrid, Diana, Mononoke, Peach{' '}
</GrammarNerd>
</InputHolder>
<InputHolder style={this.inputHolderStyle(ml3, 7)}>
<InputQuestion>Describe a large area of mass</InputQuestion>
<label>
<InputStyle
name="input7"
type="text"
placeholder="Enter a Noun"
value={this.state.value7}
onChange={this.handleSeventhChange}
ref7={input => {
this.textInput = input;
}}
/>
</label>
<GrammarNerd>
Galaxy, Planet, Wal Mart
</GrammarNerd>
</InputHolder>
<InputHolderSubmit>
<SubmitButton onClick={this.focus} type="submit" value="Submit" />
</InputHolderSubmit>
</form>
</div>
<NextQuestion>
{question}
</NextQuestion>
</MainContainer>
);
}
}
export default NameForm;
I have left some of my code I've tested (ref and autofocus) as well as some of the functions that don't seem to break the code, but are not working either.
Thank you for your help
Upvotes: 2
Views: 6791
Reputation: 6868
WORKING EXAMPLE AUTOFOCUS DEMO
The following code is a simplified sample implementation of what are you looking to do.... check it out and run it and let me know if this helps!!! Happy coding =]!
import React, { Component } from "react";
export default class AutoFocusText extends Component {
constructor() {
super();
this.state = {
active: 0,
questions: [
"how are you?",
"whats your name?",
"is reactjs awesome?"
],
value: "",
answers: []
};
this.submitHandler = this.submitHandler.bind(this);
this.renderQuestion = this.renderQuestion.bind(this);
this.onChange = this.onChange.bind(this);
}
renderQuestion() {
const { questions, active, value } = this.state;
if (active >= questions.length) return <div>You're Done!</div>;
return questions
.filter((quest, index) => index === active) // get next question
.map(quest => // map over selected question, the key prop allows react to
<FormElement // unmount and mount the components properly, thereby focussing correctly
key={active}
text={quest}
value={value}
onChange={this.onChange}
/>
);
}
onChange(e) {
this.setState({ value: e.target.value });
}
submitHandler(e) {
e.preventDefault();
const answers = [...this.state.answers, this.state.value]; //push new value to answsers array without mutation
const value = ""; // clear input
const active = this.state.active + 1; // index pointer
this.setState({ answers, value, active });
}
render() {
return (
<div>
{/* Form Wrapper */}
<form onSubmit={this.submitHandler}>
{this.renderQuestion()}
<button type="submit">Submit</button>
</form>
<ul>
{this.state.answers.map((ans, index) => {
return (
<li key={index}>
{ans}
</li>
);
})}
</ul>
</div>
);
}
}
Here is the FormElement
component that manages focusing the text input...
class FormElement extends Component {
constructor() {
super();
}
componentDidMount() {
//focus text input upon mounting component
this.textInput.focus();
}
render() {
const { text, value, onChange } = this.props;
return (
<div>
<p>
{text}
</p>
<input
ref={el => {
this.textInput = el;
}}
onChange={onChange}
type="text"
value={value}
/>
</div>
);
}
}
Upvotes: 1
Reputation: 61
For fixing consider the following
If I were you I would have a renderQuestion(step) method with a switch statement in, which returns the JSX for each case of step. Call this after the form element in your main render method with {this.renderQuestion(step)}, do the switch and this way only the active question will be rendered into the DOM. This may help making autoFocus work, I'm not sure. But to be more explicit, ref will now work properly and you can simply do
componentDidUpdate() {
this.textInput && this.textInput.focus();
}
Upvotes: 0