Reputation: 61
Application:
function TestComponent() {
const [input1, setInput1] = useState('');
const [input2, setInput2] = useState('');
const [action, setAction] = useState(null);
const onInput1Change = evt => setInput1(evt.target.value);
const onInput2Change = evt => setInput2(evt.target.value);
return (
<div>
<input type="text" value={input1} onChange={onInput1Change} />
<input type="text" value={input2} onChange={onInput2Change} />
<button type="button" onClick={() => setAction('SearchX')}>
SearchX
</button>
<button type="button" onClick={() => setAction('SearchY')}>
SearchY
</button>
<button type="button" onClick={() => setAction('Clear results')}>
Clear results
</button>
<ResultComponent input1={input1} input2={input2} action={action} />
</div>
);
}
function ResultComponent({ input1, input2, action }) {
if (action === 'SearchX') {
return <SearchX input1={input1} input2={input2} />;
}
if (action === 'SearchY') {
return <SearchY input1={input1} input2={input2} />;
}
if (action === 'Clear results') {
return null;
}
return null;
}
function SearchX({ input1, input2 }) {
const [result, setResult] = useState(null);
useEffect(() => {
// Fetch and process X-way to get the result. Using timeout to simulate that
const id = window.setTimeout(() => setResult(`Search X result with inputs: ${input1}, ${input2}`), 3000);
return () => window.clearInterval(id);
}, [input1, input2]);
return <div>{result}</div>;
}
function SearchY({ input1, input2 }) {
const [result, setResult] = useState(null);
useEffect(() => {
// Fetch and process Y-way to get the result. Using timeout to simulate that
const id = window.setTimeout(() => setResult(`Search Y result with inputs: ${input1}, ${input2}`), 3000);
return () => window.clearInterval(id);
}, [input1, input2]);
return <div>{result}</div>;
}
ReactDOM.render(<TestComponent />, document.getElementById('root'));
Problem:
We want the search to initiate only when a button is clicked. With below code, after the first search result, as soon as you change your input, the result component expectedly re-renders thereby initiating search again without button click
Steps to reproduce the problem:
Possible option:
Planning to use React.memo hook to compare action prop before updating the result component. Action prop can only change on button clicks and hence can solve the problem.
Question:
Upvotes: 1
Views: 2613
Reputation: 202608
You could, upon input interaction, reset the action back to null. This will clear out the current result and not trigger a "search".
function TestComponent() {
const [input1, setInput1] = useState('');
const [input2, setInput2] = useState('');
const [action, setAction] = useState(null);
const onInput1Change = evt => {
setInput1(evt.target.value);
setAction(null);
};
const onInput2Change = evt => {
setInput2(evt.target.value)
setAction(null);
};
return (
<div>
<input type="text" value={input1} onChange={onInput1Change} />
<input type="text" value={input2} onChange={onInput2Change} />
<button type="button" onClick={() => setAction('SearchX')}>
SearchX
</button>
<button type="button" onClick={() => setAction('SearchY')}>
SearchY
</button>
<button type="button" onClick={() => setAction(null)}>
Clear results
</button>
<ResultComponent input1={input1} input2={input2} action={action} />
</div>
);
}
EDIT Use html5 forms to save input and set action upon submit. When inputs are interacted with the inputs in state aren't updated until form is submitted.
function TestComponent() {
const [input1, setInput1] = useState("");
const [input2, setInput2] = useState("");
const [action, setAction] = useState(null);
return (
<div>
<form
id="searchX"
onSubmit={e => {
e.preventDefault();
setInput1(e.target.inputX.value);
setAction("SearchX");
}}
/>
<form
id="searchY"
onSubmit={e => {
e.preventDefault();
setInput2(e.target.inputY.value);
setAction("SearchY");
}}
/>
<input id="inputX" form="searchX" type="text" />
<input id="inputY" form="searchY" type="text" />
<input form="searchX" type="submit" value="SearchX" />
<input form="searchY" type="submit" value="SearchY" />
<button type="button" onClick={() => setAction(null)}>
Clear results
</button>
<ResultComponent input1={input1} input2={input2} action={action} />
</div>
);
}
Also, setting the "clear results" button action back to null
saves a conditional check in ResultComponent
, which simplifies to:
function ResultComponent({ input1, input2, action }) {
if (action === 'SearchX') {
return <SearchX input1={input1} input2={input2} />;
}
if (action === 'SearchY') {
return <SearchY input1={input1} input2={input2} />;
}
return null;
}
Upvotes: 1
Reputation: 4352
You can use refs to inputs and only update state on button click.
export default function TestComponent() {
const [input1, setInput1] = useState("");
const [input2, setInput2] = useState("");
const [action, setAction] = useState(null);
const input1Ref = useRef(null);
const input2Ref = useRef(null);
const onButtonClick = () => {
if (input1Ref.current) {
setInput1(input1Ref.current.value);
}
if (input2Ref.current) {
setInput2(input2Ref.current.value);
}
};
const onSearchXClick = () => {
onButtonClick();
setAction("SearchX");
};
const onSearchYClick = () => {
onButtonClick();
setAction("SearchX");
};
return (
<div>
<input ref={input1Ref} type="text" />
<input ref={input2Ref} type="text" />
<button type="button" onClick={onSearchXClick}>
SearchX
</button>
<button type="button" onClick={onSearchYClick}>
SearchY
</button>
<button type="button" onClick={() => setAction("Clear results")}>
Clear results
</button>
<ResultComponent input1={input1} input2={input2} action={action} />
</div>
);
}
Upvotes: 0