Compartir en Twitter
Go to Homepage

CÓMO CONSTRUIR UNA APLICACIÓN DE QUIZ CON REACT

November 7, 2025

Introducción a la Creación de una Aplicación de Quiz con React

React es una biblioteca de JavaScript poderosa y flexible que permite a los desarrolladores crear interfaces de usuario dinámicas e interactivas. En este tutorial, aprenderás a construir una aplicación de quiz interactiva utilizando React, enfocándote en el manejo de estados complejos, el uso de hooks como useState, y el renderizado dinámico basado en el estado de la aplicación. Este proyecto es ideal para principiantes que desean comprender cómo funcionan los estados en React, así como para desarrolladores intermedios que buscan reforzar sus conocimientos prácticos. A lo largo del tutorial, crearemos una aplicación que muestre preguntas, permita seleccionar respuestas, actualice un puntaje y muestre los resultados finales.

El objetivo principal es construir una aplicación de quiz que cumpla con los siguientes requisitos: mostrar la siguiente pregunta al hacer clic en un botón, incrementar el puntaje si la respuesta es correcta y mostrar el puntaje total al final del quiz. Para lograr esto, trabajaremos con un arreglo de preguntas, usaremos el hook useState para manejar el estado, y renderizaremos dinámicamente las preguntas y respuestas en la interfaz. Este tutorial asume conocimientos básicos de React, como componentes, JSX y el manejo de eventos, pero explicaremos cada paso detalladamente para que sea accesible.

Comencemos explorando cómo configurar el proyecto y mostrar la primera pregunta en la pantalla.

Configuración Inicial del Proyecto

Para empezar, necesitarás un proyecto de React configurado. Puedes crear uno utilizando Create React App o cualquier otra herramienta de inicialización de React. Asegúrate de tener Node.js instalado y ejecuta el siguiente comando para crear un proyecto nuevo:

npx create-react-app quiz-app
cd quiz-app
npm start

Esto iniciará un servidor de desarrollo en http://localhost:3000. Dentro del proyecto, en el archivo App.js, definiremos un arreglo de preguntas que servirá como la base de datos del quiz. Este arreglo contendrá objetos con las propiedades questionText (el texto de la pregunta) y answerOptions (un arreglo de opciones de respuesta, cada una con un texto y un booleano que indica si es correcta).

Aquí tienes un ejemplo del arreglo de preguntas:

const questions = [
    {
        questionText: "¿Cuál es la capital de Francia?",
        answerOptions: [
            { answerText: "París", isCorrect: true },
            { answerText: "Londres", isCorrect: false },
            { answerText: "Berlín", isCorrect: false },
            { answerText: "Madrid", isCorrect: false },
        ],
    },
    {
        questionText: "¿Qué lenguaje se usa para React?",
        answerOptions: [
            { answerText: "Python", isCorrect: false },
            { answerText: "JavaScript", isCorrect: true },
            { answerText: "Java", isCorrect: false },
            { answerText: "C++", isCorrect: false },
        ],
    },
    // Agrega más preguntas según sea necesario
];

Este arreglo será la fuente de datos para nuestra aplicación. Cada pregunta tiene un texto y un conjunto de opciones de respuesta, donde solo una opción es correcta (isCorrect: true).

Mostrando la Primera Pregunta

El primer paso es mostrar el texto de la primera pregunta y sus opciones de respuesta en la pantalla. En el archivo App.js, reemplaza el contenido predeterminado con un componente funcional que renderice dinámicamente la primera pregunta. Comienza accediendo al primer elemento del arreglo questions utilizando notación de puntos para obtener el texto de la pregunta.

Modifica el JSX en el componente App como sigue:

function App() {
    const questions = [
        {
            questionText: "¿Cuál es la capital de Francia?",
            answerOptions: [
                { answerText: "París", isCorrect: true },
                { answerText: "Londres", isCorrect: false },
                { answerText: "Berlín", isCorrect: false },
                { answerText: "Madrid", isCorrect: false },
            ],
        },
        // Más preguntas
    ];

    return (
        <div className="app">
            <div className="question-section">
                <div className="question-text">{questions[0].questionText}</div>
            </div>
        </div>
    );
}

export default App;

Guarda los cambios y verifica que el texto de la primera pregunta, “¿Cuál es la capital de Francia?”, se muestre en la pantalla. Aquí, questions[0].questionText accede al texto de la primera pregunta del arreglo.

Renderizando las Opciones de Respuesta

Ahora que mostramos el texto de la pregunta, necesitamos renderizar las opciones de respuesta como botones. Cada pregunta tiene un arreglo de answerOptions, y podemos usar la función map de JavaScript para iterar sobre este arreglo y crear un botón por cada opción.

Actualiza el JSX para incluir las opciones de respuesta:

function App() {
    const questions = [
        {
            questionText: "¿Cuál es la capital de Francia?",
            answerOptions: [
                { answerText: "París", isCorrect: true },
                { answerText: "Londres", isCorrect: false },
                { answerText: "Berlín", isCorrect: false },
                { answerText: "Madrid", isCorrect: false },
            ],
        },
        // Más preguntas
    ];

    return (
        <div className="app">
            <div className="question-section">
                <div className="question-text">{questions[0].questionText}</div>
                <div className="answer-section">
                    {questions[0].answerOptions.map((answerOption, index) => (
                        <button key={index}>{answerOption.answerText}</button>
                    ))}
                </div>
            </div>
        </div>
    );
}

export default App;

En este código, questions[0].answerOptions.map itera sobre las opciones de respuesta de la primera pregunta y crea un botón para cada una. La prop key={index} es necesaria para que React identifique de manera única cada elemento en la lista. Al guardar, deberías ver cuatro botones con los textos “París”, “Londres”, “Berlín” y “Madrid”.

Manejo del Estado para Cambiar Preguntas

Hasta ahora, hemos usado un índice fijo (questions[0]) para mostrar la primera pregunta. Para permitir que los usuarios avancen a la siguiente pregunta, necesitamos manejar el índice de la pregunta actual dinámicamente utilizando el hook useState de React. Este hook nos permite manejar el estado en React y actualizar la interfaz cuando el estado cambia.

Importa useState desde React y crea una variable de estado para rastrear la pregunta actual:

import { useState } from "react";

function App() {
    const [currentQuestion, setCurrentQuestion] = useState(0);
    const questions = [
        {
            questionText: "¿Cuál es la capital de Francia?",
            answerOptions: [
                { answerText: "París", isCorrect: true },
                { answerText: "Londres", isCorrect: false },
                { answerText: "Berlín", isCorrect: false },
                { answerText: "Madrid", isCorrect: false },
            ],
        },
        // Más preguntas
    ];

    return (
        <div className="app">
            <div className="question-section">
                <div className="question-text">
                    {questions[currentQuestion].questionText}
                </div>
                <div className="answer-section">
                    {questions[currentQuestion].answerOptions.map(
                        (answerOption, index) => (
                            <button key={index}>
                                {answerOption.answerText}
                            </button>
                        )
                    )}
                </div>
            </div>
        </div>
    );
}

export default App;

Aquí, currentQuestion es el índice de la pregunta actual, inicializado en 0. Al reemplazar questions[0] con questions[currentQuestion], el componente ahora renderiza la pregunta correspondiente al valor de currentQuestion. Si cambias manualmente el valor inicial de useState(0) a useState(1), verás la segunda pregunta, lo que demuestra que el renderizado es dinámico.

Avanzando a la Siguiente Pregunta

Para avanzar a la siguiente pregunta cuando el usuario hace clic en un botón de respuesta, necesitamos agregar un manejador de eventos. Crea una función handleAnswerButtonClick que incremente currentQuestion y actualice el estado con setCurrentQuestion.

Actualiza el código para incluir el manejador de eventos:

import { useState } from "react";

function App() {
    const [currentQuestion, setCurrentQuestion] = useState(0);
    const questions = [
        {
            questionText: "¿Cuál es la capital de Francia?",
            answerOptions: [
                { answerText: "París", isCorrect: true },
                { answerText: "Londres", isCorrect: false },
                { answerText: "Berlín", isCorrect: false },
                { answerText: "Madrid", isCorrect: false },
            ],
        },
        // Más preguntas
    ];

    const handleAnswerButtonClick = () => {
        const nextQuestion = currentQuestion + 1;
        setCurrentQuestion(nextQuestion);
    };

    return (
        <div className="app">
            <div className="question-section">
                <div className="question-text">
                    {questions[currentQuestion].questionText}
                </div>
                <div className="answer-section">
                    {questions[currentQuestion].answerOptions.map(
                        (answerOption, index) => (
                            <button
                                key={index}
                                onClick={handleAnswerButtonClick}
                            >
                                {answerOption.answerText}
                            </button>
                        )
                    )}
                </div>
            </div>
        </div>
    );
}

export default App;

En este código, handleAnswerButtonClick calcula el índice de la siguiente pregunta (nextQuestion) y actualiza el estado con setCurrentQuestion. El evento onClick en cada botón ejecuta esta función, avanzando a la siguiente pregunta. Sin embargo, si el usuario llega al final del arreglo de preguntas, esto causará un error porque questions[currentQuestion] intentará acceder a un índice inexistente.

Para evitar este error, agrega una verificación en handleAnswerButtonClick para asegurarte de que nextQuestion no exceda la longitud del arreglo questions:

const handleAnswerButtonClick = () => {
    const nextQuestion = currentQuestion + 1;
    if (nextQuestion < questions.length) {
        setCurrentQuestion(nextQuestion);
    } else {
        alert("Has llegado al final del quiz");
    }
};

Esta verificación asegura que solo actualizamos el estado si hay más preguntas disponibles. Por ahora, mostramos un alert cuando se llega al final, pero lo reemplazaremos con una pantalla de puntaje más adelante.

Mostrando la Pantalla de Puntaje

En lugar de un simple alert, queremos mostrar una pantalla de puntaje al final del quiz. Para esto, usaremos otro estado para controlar si mostramos las preguntas o la pantalla de puntaje. Agrega un nuevo estado showScore con useState:

import { useState } from "react";

function App() {
    const [currentQuestion, setCurrentQuestion] = useState(0);
    const [showScore, setShowScore] = useState(false);
    const questions = [
        {
            questionText: "¿Cuál es la capital de Francia?",
            answerOptions: [
                { answerText: "París", isCorrect: true },
                { answerText: "Londres", isCorrect: false },
                { answerText: "Berlín", isCorrect: false },
                { answerText: "Madrid", isCorrect: false },
            ],
        },
        // Más preguntas
    ];

    const handleAnswerButtonClick = () => {
        const nextQuestion = currentQuestion + 1;
        if (nextQuestion < questions.length) {
            setCurrentQuestion(nextQuestion);
        } else {
            setShowScore(true);
        }
    };

    return (
        <div className="app">
            {showScore ? (
                <div className="score-section">Has terminado el quiz</div>
            ) : (
                <div className="question-section">
                    <div className="question-text">
                        {questions[currentQuestion].questionText}
                    </div>
                    <div className="answer-section">
                        {questions[currentQuestion].answerOptions.map(
                            (answerOption, index) => (
                                <button
                                    key={index}
                                    onClick={handleAnswerButtonClick}
                                >
                                    {answerOption.answerText}
                                </button>
                            )
                        )}
                    </div>
                </div>
            )}
        </div>
    );
}

export default App;

Aquí, usamos un operador ternario en el JSX para renderizar condicionalmente. Si showScore es true, mostramos la sección de puntaje; de lo contrario, mostramos la sección de preguntas. Cuando el usuario llega al final del quiz, setShowScore(true) activa la pantalla de puntaje.

Guardando y Actualizando el Puntaje

Para hacer el quiz más interactivo, necesitamos rastrear el puntaje del usuario e incrementarlo cuando selecciona una respuesta correcta. Cada opción de respuesta tiene una propiedad isCorrect que indica si es correcta. Usaremos esta propiedad para actualizar el puntaje.

Primero, crea un nuevo estado para el puntaje:

const [score, setScore] = useState(0);

Luego, modifica handleAnswerButtonClick para aceptar el parámetro isCorrect y actualizar el puntaje si la respuesta es correcta. También ajusta el evento onClick para pasar el valor isCorrect:

import { useState } from "react";

function App() {
    const [currentQuestion, setCurrentQuestion] = useState(0);
    const [showScore, setShowScore] = useState(false);
    const [score, setScore] = useState(0);
    const questions = [
        {
            questionText: "¿Cuál es la capital de Francia?",
            answerOptions: [
                { answerText: "París", isCorrect: true },
                { answerText: "Londres", isCorrect: false },
                { answerText: "Berlín", isCorrect: false },
                { answerText: "Madrid", isCorrect: false },
            ],
        },
        // Más preguntas
    ];

    const handleAnswerButtonClick = (isCorrect) => {
        if (isCorrect) {
            setScore(score + 1);
        }
        const nextQuestion = currentQuestion + 1;
        if (nextQuestion < questions.length) {
            setCurrentQuestion(nextQuestion);
        } else {
            setShowScore(true);
        }
    };

    return (
        <div className="app">
            {showScore ? (
                <div className="score-section">Has terminado el quiz</div>
            ) : (
                <div className="question-section">
                    <div className="question-text">
                        {questions[currentQuestion].questionText}
                    </div>
                    <div className="answer-section">
                        {questions[currentQuestion].answerOptions.map(
                            (answerOption, index) => (
                                <button
                                    key={index}
                                    onClick={() =>
                                        handleAnswerButtonClick(
                                            answerOption.isCorrect
                                        )
                                    }
                                >
                                    {answerOption.answerText}
                                </button>
                            )
                        )}
                    </div>
                </div>
            )}
        </div>
    );
}

export default App;

En este código, si isCorrect es true, incrementamos el puntaje con setScore(score + 1). El valor isCorrect se pasa desde el botón correspondiente, permitiendo que nuestra función actualice el puntaje dinámicamente.

Mostrando el Puntaje Final

Ahora que guardamos el puntaje, podemos mostrarlo en la pantalla de puntaje. Actualiza la sección de puntaje en el JSX para incluir el puntaje y el número total de preguntas:

return (
    <div className="app">
        {showScore ? (
            <div className="score-section">
                Has puntuado {score} de {questions.length}
            </div>
        ) : (
            <div className="question-section">
                <div className="question-text">
                    {questions[currentQuestion].questionText}
                </div>
                <div className="answer-section">
                    {questions[currentQuestion].answerOptions.map(
                        (answerOption, index) => (
                            <button
                                key={index}
                                onClick={() =>
                                    handleAnswerButtonClick(
                                        answerOption.isCorrect
                                    )
                                }
                            >
                                {answerOption.answerText}
                            </button>
                        )
                    )}
                </div>
            </div>
        )}
    </div>
);

Este cambio muestra el puntaje del usuario y el total de preguntas, como “Has puntuado 3 de 5” si el usuario acertó tres preguntas de cinco.

Mostrando el Número de Pregunta Actual

Finalmente, queremos que la interfaz muestre el número de la pregunta actual, como “Pregunta 1 de 5”. Actualmente, esto podría estar hardcoded, pero lo haremos dinámico usando currentQuestion. Actualiza el JSX para incluir el contador de preguntas:

return (
    <div className="app">
        {showScore ? (
            <div className="score-section">
                Has puntuado {score} de {questions.length}
            </div>
        ) : (
            <div className="question-section">
                <div className="question-count">
                    Pregunta {currentQuestion + 1} de {questions.length}
                </div>
                <div className="question-text">
                    {questions[currentQuestion].questionText}
                </div>
                <div className="answer-section">
                    {questions[currentQuestion].answerOptions.map(
                        (answerOption, index) => (
                            <button
                                key={index}
                                onClick={() =>
                                    handleAnswerButtonClick(
                                        answerOption.isCorrect
                                    )
                                }
                            >
                                {answerOption.answerText}
                            </button>
                        )
                    )}
                </div>
            </div>
        )}
    </div>
);

Sumamos 1 a currentQuestion porque los índices en JavaScript comienzan en 0, pero para los usuarios, las preguntas comienzan en 1. Esto asegura que la interfaz muestre “Pregunta 1” en lugar de “Pregunta 0”.

Mejorando la Experiencia del Usuario

Para mejorar la aplicación, considera agregar estilos CSS para hacerla más atractiva. Crea un archivo App.css y aplica estilos básicos:

.app {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: #f0f0f0;
}

.question-section {
    background-color: white;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

.question-text {
    font-size: 1.5em;
    margin-bottom: 20px;
}

.answer-section {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.answer-section button {
    padding: 10px;
    font-size: 1em;
    cursor: pointer;
    border: none;
    background-color: #007bff;
    color: white;
    border-radius: 4px;
}

.answer-section button:hover {
    background-color: #0056b3;
}

.score-section {
    font-size: 1.5em;
    text-align: center;
}

Importa este archivo en App.js con import './App.css';. Estos estilos centran la aplicación, agregan un fondo blanco a la sección de preguntas y estilizan los botones para una mejor experiencia visual.

Agregando Funcionalidades Avanzadas

Para llevar la aplicación al siguiente nivel, puedes implementar las siguientes mejoras:

  1. Reiniciar el Quiz: Agrega un botón en la pantalla de puntaje para reiniciar el quiz, restableciendo currentQuestion, score y showScore:
const handleRestartQuiz = () => {
    setCurrentQuestion(0);
    setScore(0);
    setShowScore(false);
};

Agrega el botón en la sección de puntaje:

<div className="score-section">
    Has puntuado {score} de {questions.length}
    <button onClick={handleRestartQuiz}>Reiniciar Quiz</button>
</div>
  1. Feedback Inmediato: Muestra un mensaje temporal cuando el usuario selecciona una respuesta correcta o incorrecta. Agrega un nuevo estado para el mensaje:
const [feedback, setFeedback] = useState("");

const handleAnswerButtonClick = (isCorrect) => {
    if (isCorrect) {
        setScore(score + 1);
        setFeedback("¡Correcto!");
    } else {
        setFeedback("Incorrecto");
    }
    setTimeout(() => {
        setFeedback("");
        const nextQuestion = currentQuestion + 1;
        if (nextQuestion < questions.length) {
            setCurrentQuestion(nextQuestion);
        } else {
            setShowScore(true);
        }
    }, 1000);
};

Agrega el feedback en el JSX:

<div className="question-section">
    <div className="question-count">
        Pregunta {currentQuestion + 1} de {questions.length}
    </div>
    <div className="question-text">
        {questions[currentQuestion].questionText}
    </div>
    {feedback && <div className="feedback">{feedback}</div>}
    <div className="answer-section">
        {questions[currentQuestion].answerOptions.map((answerOption, index) => (
            <button
                key={index}
                onClick={() => handleAnswerButtonClick(answerOption.isCorrect)}
            >
                {answerOption.answerText}
            </button>
        ))}
    </div>
</div>
  1. Temporizador por Pregunta: Agrega un temporizador que limite el tiempo para responder cada pregunta. Usa useEffect para implementar un temporizador:
import { useState, useEffect } from "react";

function App() {
    const [currentQuestion, setCurrentQuestion] = useState(0);
    const [showScore, setShowScore] = useState(false);
    const [score, setScore] = useState(0);
    const [timeLeft, setTimeLeft] = useState(10);

    const questions = [
        // ... preguntas
    ];

    useEffect(() => {
        if (timeLeft === 0) {
            const nextQuestion = currentQuestion + 1;
            if (nextQuestion < questions.length) {
                setCurrentQuestion(nextQuestion);
                setTimeLeft(10);
            } else {
                setShowScore(true);
            }
        }
        const timer =
            timeLeft > 0 && setInterval(() => setTimeLeft(timeLeft - 1), 1000);
        return () => clearInterval(timer);
    }, [timeLeft, currentQuestion, questions.length]);

    const handleAnswerButtonClick = (isCorrect) => {
        if (isCorrect) {
            setScore(score + 1);
        }
        setTimeLeft(10);
        const nextQuestion = currentQuestion + 1;
        if (nextQuestion < questions.length) {
            setCurrentQuestion(nextQuestion);
        } else {
            setShowScore(true);
        }
    };

    return (
        <div className="app">
            {showScore ? (
                <div className="score-section">
                    Has puntuado {score} de {questions.length}
                </div>
            ) : (
                <div className="question-section">
                    <div className="question-count">
                        Pregunta {currentQuestion + 1} de {questions.length}
                    </div>
                    <div className="question-text">
                        {questions[currentQuestion].questionText}
                    </div>
                    <div>Tiempo restante: {timeLeft} segundos</div>
                    <div className="answer-section">
                        {questions[currentQuestion].answerOptions.map(
                            (answerOption, index) => (
                                <button
                                    key={index}
                                    onClick={() =>
                                        handleAnswerButtonClick(
                                            answerOption.isCorrect
                                        )
                                    }
                                >
                                    {answerOption.answerText}
                                </button>
                            )
                        )}
                    </div>
                </div>
            )}
        </div>
    );
}

export default App;

Estas mejoras hacen que la aplicación sea más robusta y atractiva, proporcionando una experiencia de usuario más rica.

Conclusiones

Construir una aplicación de quiz con React es una excelente manera de aprender a manejar estados, trabajar con hooks como useState y useEffect, y renderizar contenido dinámicamente. A lo largo de este tutorial, hemos creado una aplicación que muestra preguntas, permite seleccionar respuestas, actualiza un puntaje y muestra los resultados finales. También hemos explorado cómo mejorar la aplicación con estilos, feedback inmediato, un botón de reinicio y un temporizador. Este proyecto no solo refuerza los fundamentos de React, sino que también te prepara para abordar proyectos más complejos que involucren estados y eventos. Te animamos a experimentar con el código, agregar más preguntas o implementar nuevas funcionalidades para personalizar la aplicación según tus necesidades.