Search code examples
javascriptreactjstypescriptreact-hooksreact-typescript

How to change icon in an array in react typescript


I am trying to create accordion that when you click it will slide down and change the icon

Here is my following code in App.tsx:

const accordionList = [
  {
    title: "Horses can..."
    icon: "🐴",
    pointer: "👈"
  },
  {
    title: "Rhino skin maybe.."
    icon: "🦏",
    pointer: "👈"
  },
  {
    title: "If you looking..."
    icon: "🦄",
    pointer: "👈"
  }
]

function App() {
  const [icon, setIcon] = useState(['👈'])
  const toggleAccordion = (accordion: any, index: any) => {
    const iconRotate = [...accordion.pointer];
    iconRotate[accordion.pointer] = '👇';
    setIcon(iconRotate[accordion.pointer])
    accordion.pointer = icon;

  }

  
  return (
    <div className="App">
      <div className="App-header">
        {accordionList.map((accordion, index) => (
          <ul className="accordion-list" key={index}>
            <div className="accordion-item">
              <button onClick={() => toggleAccordion(accordion, index)}>{accordion.icon} {icon} </button>
              <Accordion accordion={accordion} />
            </div>
          </ul>
        ))}
      </div>
    </div>
  );
}

export default App;

and here is my following code in Accordion.tsx:

interface Props {
  accordion: { title: string, icon: string;}
}

export const Accordion: React.FC<Props> = (props) => {

  const { accordion } = props;

  return (
      <div>
        {accordion.title}
      </div>
    )
};

Right now my toggleAccordion function didn't change the icon when it clicked. How can I improve my function?

Updated pointer to array of objects


Solution

  • From my understanding of what you're trying to accomplish, to change the icon, you just need to update the icon state rather than updating the pointer inside your accordionList.pointer and maintain this icon state for all the item in the icon list you have. I'll suggest you to divide your accordion item into its own component with the icon state like below:

    The App.js file:

    const accordionList = [
      {
        title: "Horses can..."
        icon: "🐴",
        pointer: "👈"
      },
      {
        title: "Rhino skin maybe.."
        icon: "🦏",
        pointer: "👈"
      },
      {
        title: "If you looking..."
        icon: "🦄",
        pointer: "👈"
      }
    ]
    
    function App() {
      return (
        <div className="App">
          <div className="App-header">
            {accordionList.map((accordion, index) => (
              <ul className="accordion-list" key={index}>
                <AccordionItem accordion={accordion}/>
              </ul>
            ))}
          </div>
        </div>
      );
    }
    
    export default App;

    The AccordionItem component:

    function AccordionItem({accordion}) {
      const [indicatorIcon, setIndicatorIcon] = useState('👈')
      const toggleAccordion = () => {
         setIndicatorIcon('👇');
      }
      return (
        <div className="accordion-item">
            <button 
             onClick={() => toggleAccordion(accordion, index)}
            >{accordion.icon} {icon} </button>
            <Accordion accordion={accordion} />
        </div>
      );
    }
    
    export default App;

    And you can keep your Accordion as is. This will separate your concern, help you to only need to care about the state of the AccordionItem, and this will remove any unnecessary complex array manipulation.