Я пытаюсь найти способ динамически вызывать функцию с учетом строки или ссылаться на переменную с учетом строки. Например:

import React, {useState} from 'react'

const array = [
  {
    text: 'count1',
    setFunctionName: 'setCount1',
    otherdata: {}
  },
  {
    text: 'count2',
    setFunctionName: 'setCount2',
    otherdata: {}
  }
]

const myFunction = () => {
  const  [count1, setCount1] = useState(0)
  const  [count2, setCount2] = useState(0)

  return(
     <div>
       {array.map((item) => {
          // I want to use item.text to reference the correct count variable
          // I want to use item.setFunctionName to change the correct count 
        })}
     </div>
  )
}

Конкретный вариант использования: я хочу создать многоразовое меню боковой панели, где данные для ссылок хранятся в массиве объектов в отдельном файле. У некоторых пунктов меню есть сворачиваемые подменю, и мне нужно управлять открытием и закрытием подменю с помощью состояния. пример:

import { Button, Collapse } from 'react-bootstrap'

function Example() {
      const [open, setOpen] = useState(false);
    
      return (
        <>
          <Button
            onClick={() => setOpen(!open)} //**I want to dynamically set this when mapping over the array**
          >
            click
          </Button>
          <Collapse in={open}> //**I want to dynamically set this when mapping over the array**
            <div id="example-collapse-text">
              This is example collapsed text
            </div>
          </Collapse>
        </>
      );
    }
0
CherryPoppins 21 Янв 2021 в 01:37

4 ответа

Лучший ответ

Если вы не хотите полностью перемещаться в useReducer, то также нормально удерживать объект в useState вместо того, чтобы иметь отдельные вызовы useState для каждого отдельного поля:

  import React, {useState} from 'react'

  const array = [
    {
      text: 'count1',
      otherdata: {}
    },
    {
      text: 'count2',
      otherdata: {}
    }
  ]

  const myFunction = () => {
    // This could even be initialised based on the items in `array`
    const  [count, setCount] = useState({
      count1: 0,
      count2: 0,
    });

    return(
       <div>
         {array.map((item, index) => {
            // I want to use item.text to reference the correct count variable
            const countValue = count[item.text];
            
            const updateCount = () => {
              setCount((prevCount) => {
                return {
                  ...prevCount,
                  [item.text]: prevCount[item.text] + 1,
                };
              });
            };
            
            return (
              <button key={index}
                onClick={updateCount}
              >{item.text}: { countValue }</button>
            );
          })}
       </div>
    )
  }
1
Sly_cardinal 20 Янв 2021 в 23:43

Может как то так?

Песочница

import React, { useState } from "react";

const stepOne = () => "This is the step number 1";
const stepTwo = () => "This is the step number 2";
const stepTree = () => "This is the step number 3";

const objectOfFunctions = {
  stepOne,
  stepTwo,
  stepTree
};

const arrayOfStrings = [
  "stepOne",
  "stepTwo",
  "stepTree"
];

export default function App() {
  return <div>
    {
      arrayOfStrings.map( (e, index) => (
        <h3 key={index}>
          {objectOfFunctions[e]()}
        </h3>
      ))
    }
  </div>;
}

Или вы можете преобразовать свою строку в JSX во время выполнения с помощью этого компонента: response-jsx-parser

Таким образом, у вас может быть такая строка:

const str = 'This is a component rendered in runtime {Component}'

И это сработает.

0
DoRyu Dracken 20 Янв 2021 в 23:12

Возможно, я ошибаюсь, но похоже, что если вы используете response-bootstrap, вы можете использовать компонент аккордеона, поскольку он, вероятно, будет выглядеть и вести себя ближе к тому, что вы ищете. Но в любом случае заполнение того, что показывает, когда оно развернуто, будет обрабатываться аналогичным образом. Если содержимое вашего раскрытия сохраняется в объекте состояния, оно должно автоматически обновляться при изменении состояния. Вы можете сопоставить данный массив и отобразить JSX как таковой

<Collapse in={open}> //**I want to dynamically set this when mapping over the array**
    array.map(element => {
        return(
            <div>
                {element.text
                 element.setFunctionName}
            </div>
        )
    })
</Collapse>

Это очень упрощенное применение этого процесса. Здесь следует отметить, что если вы отображаете массив объектов, вы можете визуализировать JSX для каждого элемента массива. Это может быть любой JSX, даже созданные вами компоненты. Это довольно мощно, поскольку эти компоненты могут сохранять свое собственное состояние и поведение и будут вести себя как таковые в свернутой области.

Кроме того, если эти внутренние объекты массива также должны иметь функцию сворачивания, вы можете сделать такой же точный JSX внутри сопоставленного элемента. В этом отношении нет предела.

0
James Courcelle 20 Янв 2021 в 23:07

Вероятно, лучший способ добиться этого - использовать редуктор.

https://reactjs.org/docs/hooks-reference.html#usereducer

Что-то вроде этого, может быть?

const initialState = {count1: 0, count2: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'setCount1':
      return {
        ...state,
        count1: action.value
      };
    case 'setCount2':
      return {
        ...state,
        count2: action.value
      };
    default:
      throw new Error();
  }
}

const array = [
  {
    text: 'count1',
    setFunctionName: 'setCount1',
    otherdata: {}
  },
  {
    text: 'count2',
    setFunctionName: 'setCount2',
    otherdata: {}
  }
]

const myFunction = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return(
     <div>
       {array.map((item) => {
          return <a onClick={ () => dispatch({ type: item.setFunctionName, value:3 }) }>{state[item.text]} <a/>
        })}
     </div>
  )
}
2
Sly_cardinal 20 Янв 2021 в 23:33