React点击按钮重复生成组件:三个实现函数哪个更接近解决方案?
Hey Janakanty, let's break down your current code and fix this dynamic component addition issue—you're already thinking in the right direction, just missing some key React patterns!
What's Wrong With Your Current Functions
Let's go through each one to understand why they aren't working as expected:
Addingfunction:- You've defined
componentsas an object ({ button : Button }), butmap()only works on arrays. - The function doesn't return any JSX, and there's no state to track how many components should be rendered—so even if it did work, nothing would show up.
- You've defined
AddQfunction:- This uses raw DOM manipulation (
document.createElement,appendChild), which is an anti-pattern in React. React relies on its virtual DOM to sync state and UI, so directly modifying the DOM will break this sync and cause hard-to-debug issues later. - The
countervariable is declared inside the function, so it resets to 0 every time you click the button—you'll always get "przykładowy tekst 1" instead of incrementing numbers.
- This uses raw DOM manipulation (
AddQuestionfunction:ReactDOM.render()replaces the entire contents of the target element (#ankieta) every time it's called, which is why you only ever see one button. It doesn't append new components—it overwrites what's already there.
The Correct React Approach: Use State to Track Components
React is declarative, so we use state to track how many (or which) components we want to render, then let React handle updating the UI automatically. Here's a working solution tailored to your questionnaire use case:
Step 1: Define Your Custom Question Component
First, let's create a reusable component for your questionnaire input (replace this with your actual Button or input component):
// Your custom question component (adjust this to match your needs) const QuestionComponent = ({ questionId }) => ( <div className="question-container"> <h4>Question {questionId}</h4> <input type="text" placeholder="Enter your question here" /> {/* Add any other inputs/options you need for the question */} </div> );
Step 2: Build the Main Component with State
We'll use useState to keep track of our list of questions. Each time the button is clicked, we add a new entry to the state array, and React will render all components in the array:
import { useState } from 'react'; const QuestionnaireBuilder = () => { // State to track our list of questions (we'll use unique IDs for each) const [questions, setQuestions] = useState([]); // Function to add a new question when the button is clicked const handleAddQuestion = () => { // Generate a unique ID for the new question (using array length + 1 for simplicity) const newQuestionId = questions.length + 1; // Update state by spreading the existing questions and adding the new ID setQuestions(prevQuestions => [...prevQuestions, newQuestionId]); }; return ( <div className="questionnaire-builder"> {/* Button to trigger adding a new question */} <button onClick={handleAddQuestion} style={{ padding: '8px 16px', marginBottom: '16px' }}> Add New Question </button> {/* Container where questions will be rendered */} <div id="ankieta"> {/* Map over the questions array to render each QuestionComponent */} {questions.map(questionId => ( <QuestionComponent key={questionId} questionId={questionId} /> ))} </div> </div> ); }; export default QuestionnaireBuilder;
Key Concepts Explained
- State Management: The
questionsstate array tracks all the questions we want to render. When we callsetQuestions, React re-renders the component with the updated array. - Declarative Rendering: Instead of manually manipulating the DOM, we tell React "here's the list of questions, render each one as a
QuestionComponent". React takes care of updating the UI efficiently. - Unique Keys: We use
questionIdas thekeyprop for each component—this helps React identify which components have changed, been added, or removed, ensuring smooth re-renders.
If you need to store more data for each question (like default text, answer type, etc.), you can modify the state to store objects instead of just IDs. For example:
// Adding a question with more data setQuestions(prev => [...prev, { id: newQuestionId, type: 'text', label: '' }]);
Then you can pass that data to your QuestionComponent to pre-fill or customize it!
内容的提问来源于stack exchange,提问作者Janakanty

