Problem
I’m trying to build a dynamic questionnaire component in React where users can add and remove question sections. Each section manages its own local state using hooks (like useState or useReducer), and I store each section in an array of React components.
What I tried and expected
I have a QuestionSection component that manages its own state:
function QuestionSection({ id }) {
const [answer, setAnswer] = useState("");
return (
<div>
<h3>Question {id + 1}</h3>
<p>Current answer: "{answer}"</p>
<input
type="text"
value={answer}
onChange={(e) => setAnswer(e.target.value)}
/>
</div>
);
}
And a parent component that manages the list:
function Questionnaire() {
const [sections, setSections] = useState([0]); // array of section ids
const add = () => setSections(prev => [...prev, prev.length]);
const remove = () => setSections(prev => prev.slice(0, -1));
return (
<div>
<button onClick={add}>Add Section</button>
<button onClick={remove}>Remove Section</button>
{sections.map(id => (
<QuestionSection key={id} id={id} />
))}
</div>
);
}
Expected behavior: Only the removed section should reset; other sections should keep their entered data.
Actual behavior: When I remove a section (especially from the middle), other sections lose their state and reset to empty.
What I’ve tried
- Ensured
key={id}is unique and consistent - Tried using UUID for IDs instead of sequential numbers – same issue
- Wrapped QuestionSection in React.memo() and added debug logs – state still resets
The keys appear stable in my logging, but React still resets the internal hook state. What am I missing about how React reconciliation works with dynamic lists?
Should I be lifting state up instead? Any recommended patterns?