I can't seem to resolve an infinite loop issue in my react project.
I'm working on a daily-log react app. Let me explain the project briefly. Here is the picture of the code for quick view:
The same code is available at the bottom.
The structure (from top to bottom)
DailyLog
component has a form that uses Question
components into which props are passed.Question
component uses the props to display a question and description. It also contains an Input
component into which props are further passed down.Input
component takes the props and renders the appropriate form input field.The logic (from bottom to top)
Input
component handles it's own inputState
. The state is changed when the user inputs something and the onChangeHandler
is triggered.Input
component also has a useEffect()
hook that calls an onInput()
function that was passed down as props from DailyLog
.onInputHandler()
in the DailyLog
component updates the formState
which is the form-wide state containing all input field values. The formState
is amended depending on which input field is filled at the time.onInputHandler()
uses the useCallback()
hook which is supposed to stop an infinite loop caused by any parent/child re-renders. But it doesn't work :frowning: What's wrong in the code? What am I missing here? Code provided below:
//DailyLog.js
import React, { useState, useCallback } from 'react';
import Question from '../components/FormElements/Question';
import questionData from '../components/DailyLog/questionData';
import './DailyLog.css';
const DailyLog = () => {
const [formState, setFormState] = useState();
const onInputHandler = useCallback(
(inputId, inputValue) => {
setFormState({
...formState,
[inputId]: inputValue,
});
},
[formState]
);
return (
<main className="container">
<form action="" className="form">
<Question
id="title"
element="input"
type="text"
placeholder="Day, date, calendar scheme"
onInput={onInputHandler}
/>
<Question
id="focus"
question={questionData.focusQuestion}
description={questionData.focusDescription}
element="textarea"
placeholder="This month's focus is... This week's focus is..."
onInput={onInputHandler}
/>
</form>
</main>
);
};
export default DailyLog;
//Question.js
import React from 'react';
import Input from './Input';
import './Question.css';
const Question = props => {
return (
<div className="form__group">
{props.question && (
<label className="form__label">
<h2>{props.question}</h2>
</label>
)}
<small className="form__description">{props.description}</small>
<Input
id={props.id}
element={props.element}
type={props.type}
placeholder={props.placeholder}
onInput={props.onInput}
/>
</div>
);
};
export default Question;
//Input.js
import React, { useState, useEffect } from 'react';
import './Input.css';
const Input = props => {
const [inputState, setInputState] = useState();
const { id, onInput } = props;
useEffect(() => {
onInput(id, inputState);
}, [id, onInput, inputState]);
const onChangeHandler = event => {
setInputState(event.target.value);
};
// check if question element type is for input or textarea
const element =
props.element === 'input' ? (
<input
id={props.id}
className="form__field"
type={props.type}
value={inputState}
placeholder={props.placeholder}
onChange={onChangeHandler}
/>
) : (
<textarea
id={props.id}
className="form__field"
rows="1"
value={inputState}
placeholder={props.placeholder}
onChange={onChangeHandler}
/>
);
return <>{element}</>;
};
export default Input;
Remove id and onInput from useEffect sensivity list
useEffect(() => {
onInput(id, inputState);
}, [inputState]);
And set default value of inputState to '' as follow:
const [inputState, setInputState] = useState('');
To prevent 'A component is changing an uncontrolled input of type text to be controlled error in ReactJS'. Also you can init formState:
const [formState, setFormState] = useState({title:'', focus:''});