javascriptreactjsstate-management

My object goes to its initial value on state update


I'm trying to create tabs in react like this: enter image description here I want tabs not to be visible unless the previous component was completed or the button was clicked.

import { FaCheckCircle } from "react-icons/fa";
import Inicio from "./Inicio";
import Incluido from "./Incluido";
import Descripcion from "./Descripcion";
import React, { useState } from "react";
import { FaBookOpen } from "react-icons/fa";
import { FaCheckDouble } from "react-icons/fa";
function Home() {
    const [toggle, setToggle] = useState(0)
    const [opciones, setOpciones] = useState([{
        id: 0,
        texto: "Inicio",
        visibilidad: "",
        descripcionTitulo: "Bienvenido al programa de carga de Viajes",
        descripcion: "Selecciona lo que quieras hacer",
        icon: <FaCheckCircle />,
        next: next,
        contenido: <Inicio setToggle={setToggle} />,
        botonSiguiente: "Nuevo Viaje"
    },
    {
        id: 1,
        texto: "Descripción",
        visibilidad: "aa",
        descripcionTitulo: "Descripcion",
        descripcion: "detalles importantes",
        icon: <FaBookOpen />,
        next: next,
        contenido: <Descripcion setToggle={setToggle} />,
        botonSiguiente: "Siguiente"
    },
    {
        id: 2,
        texto: "Incluido / no",
        visibilidad: "hidden",
        descripcionTitulo: "Incluido/ No incluido",
        descripcion: "Si el servicio no está seleccionado, el cliente lo verá como no incluido.",
        icon: <FaCheckDouble />,
        next: next,
        contenido: <Incluido setToggle={setToggle} />,
        botonSiguiente: "Siguiente"

    }
    ])
    function next(next) {
        if (opciones[next.id + 1]) {
            setToggle(next.id + 1)
            setOpciones(opciones.map(e => (e.id === next.id + 1 ? { ...e, visibilidad: "descripcion 1" } : e)))
        }
    }

    return (
        <div className="App min-h-[100vh] grid grid-cols-12 gap-3 flex-row p-5 bg-slate-900">
            <aside className='col-span-4 md:col-span-4 lg:col-span-2 min-h-[50%] bg-slate-800 text-start border-l-2 border-slate-600'>
                <ul className='p-5 text-white font-semibold flex flex-col space-x-5'>
                    {opciones.map((opcion) => (
                        <div className={opcion.visibilidad} key={opcion.id}>
                            <li className="flex flex-row cursor-pointer mt-1" onClick={() => setToggle(opcion.id)}>
                                <span className='text-xl mr-2'> {opcion.icon} </span>
                                <span>  {opcion.texto} </span>
                            </li>
                        </div>
                    ))}
                </ul>
            </aside>
            <section className='col-span-8 md:col-span-8 lg:col-span-10 min-h-[50%] bg-slate-800 p-5'>
                {opciones.map((opcion) => (
                    <div key={opcion.id} className={toggle === opcion.id ? "show-content" : "content"}>
                        <div className='border-b text-left border-cyan-400 w-full pb-5'>
                            <h2 className='text-2xl font-semibold text-white'>
                                {opcion.descripcionTitulo}
                            </h2>
                            <span className='text-white'>
                                {opcion.descripcion}
                            </span>
                        </div>
                        {opcion.contenido}
                        <div className='mt-3 flex justify-end w-full'>
                            <button className='bg-cyan-500 text-white font-semibold p-4 rounded' onClick={() => opcion.next(opcion)}>
                                {opcion.botonSiguiente}
                            </button>
                        </div>
                    </div>
                ))}
            </section>
        </div >
    );
}

export default Home;

When I click on the button in the first component "descripción" is displayed but then when i try to click on the button in the "descripción" component it goes back to being hidden. I'm not sure how to handle this?

Everytime "next" function is called it should check if there's another component next in the array, display it and display the tab list title also but it's not working since on every rerender it goes back being hidden.


Solution

  • is this the behaviour you are looking for?

        const App = () => {
          const [clickedOpciones, setClickedOpciones] = useState([0]);
          const [toggle, setToggle] = useState(0);
          const [opciones] = useState([
            {
              id: 0,
              texto: 'Inicio',
              visibilidad: '',
              descripcionTitulo: 'Bienvenido al programa de carga de Viajes',
              descripcion: 'Selecciona lo que quieras hacer',
              botonSiguiente: 'Nuevo Viaje',
            },
            {
              id: 1,
              texto: 'Descripción',
              visibilidad: '',
              descripcionTitulo: 'Descripción',
              descripcion: 'Detalles importantes',
              botonSiguiente: 'Siguiente',
            },
            {
              id: 2,
              texto: 'Incluido / no',
              visibilidad: '',
              descripcionTitulo: 'Incluido/ No incluido',
              descripcion:
                'Si el servicio no está seleccionado, el cliente lo verá como no incluido.',
              botonSiguiente: 'Termino',
            },
          ]);
    
          function next(opcionActual) {
            const nextIndex = opcionActual.id + 1;
            if (nextIndex < opciones.length) {
              setToggle(nextIndex);
            }
            if (!clickedOpciones.includes(nextIndex)) {
              setClickedOpciones((prev) => [...prev, nextIndex]);
            }
          }
    
    
          return (
            <div className="App min-h-[100vh] grid grid-cols-12 gap-3 flex-row p-5 bg-slate-900">
              <aside className="col-span-4 md:col-span-4 lg:col-span-2 min-h-[50%] bg-slate-800 text-start border-l-2 border-slate-600">
                <ul className="p-5 text-white font-semibold flex flex-col space-y-5">
                  {opciones.map((opcion) => (
                    <div className={opcion.visibilidad} key={opcion.id}>
                      {(opcion.id === 0 || clickedOpciones.includes(opcion.id)) && (
                        <li
                          className="flex flex-row cursor-pointer mt-1"
                          onClick={() => (next(opcion.id), setToggle(opcion.id))}
                        >
                          <span className="text-xl mr-2">
                            {/* Add your icon here if needed */}
                          </span>
                          <span>{opcion.texto}</span>
                        </li>
                      )}
                    </div>
                  ))}
                </ul>
              </aside>
              <section className="col-span-8 md:col-span-8 lg:col-span-10 min-h-[50%] bg-slate-800 p-5">
                {opciones.map((opcion) => (
                  <div
                    key={opcion.id}
                    className={toggle === opcion.id ? 'show-content' : 'hidden'}
                  >
                    {toggle === opcion.id && (
                      <>
                        <div className="border-b text-left border-cyan-400 w-full pb-5">
                          <h5 className="text-2xl font-semibold text-white">
                            {opcion.descripcionTitulo}
                          </h5>
                          <p className="text-white">{opcion.descripcion}</p>
                        </div>
    
                        <div className="mt-3 flex justify-end w-full">
                          <button
                            className="bg-cyan-500 text-white font-semibold p-4 rounded"
                            onClick={() => next(opcion)}
                          >
                            {opcion.botonSiguiente}
                          </button>
                        </div>
                      </>
                    )}
                  </div>
                ))}
              </section>
            </div>
          );
        };
    
        export default App;