Как забрать значение с активной кнопки?

Ребята, подскажите по Реакту. Функциональный компонент отрисовывает 3 импута (type=range) + 4 кнопки (одна из 4 кнопок активна, массив isActive) + компонент total (собирает значения со всех импутов и кнопок, согласно формуле = (height + weight - age) * ratio). Вопрос: Как правильно написать переменную ratio, чтобы она забирала значение "ratio" активной кнопки из массива isActive?

export default function Component() {
    const [ranges, setRanges] = useState([
        {
            id: 'height',
            min: 50,
            max: 220,
            label: 'height (sm)',
            lng: 'lng-height',
            value: 135,
        },
        {
            id: 'weight',
            min: 1,
            max: 200,
            label: 'weight (kg)',
            lng: 'lng-weight',
            value: 100,
        },
        { id: 'age', min: 1, max: 110, label: 'age', lng: 'lng-age', value: 55 },
    ]);

    const [isActive, setActive] = useState([
        { id: 'a0', status: true, name: 'low', ratio: 1.2 },
        { id: 'a1', status: false, name: 'small', ratio: 1.375 },
        { id: 'a2', status: false, name: 'medium', ratio: 1.55 },
        { id: 'a3', status: false, name: 'high', ratio: 1.725 },
    ]);

    const chooseRange = (id, value) => {
        setRanges(
            ranges.map((item) => (item.id === id ? { ...item, value } : item))
        );
    };

    const chooseActive = (id) => {
        setActive(
            isActive.map((item) =>
                item.id === id ? { ...item, status: true } : { ...item, status: false }
            )
        );
    };

    const height = ranges[0].value;
    const weight = ranges[1].value;
    const age = ranges[2].value;

    let ratio = ????;

    const total = (height + weight - age) * ratio;

    return (
        <section id='component'>
                        <div className='line line2'>
                            <div className='ranges df'>
                                {ranges.map((item) => (
                                    <RangeInput
                                        key={item.id}
                                        onChange={chooseRange}
                                        value={item.value}
                                        {...item}
                                    />
                                ))}
                            </div>
                        </div>
                        <div className='line line3'>
                            <div className='btns df fww'>
                                {isActive.map((item) => (
                                    <Btn
                                        key={item.id}
                                        onClick={() => chooseActive(item.id)}
                                        ratio={item.ratio}
                                        className={
                                            item.status
                                                ? `btn lng-${item.name} active`
                                                : `btn lng-${item.name}`
                                        }>
                                        {item.name}
                                    </Btn>
                                ))}
                            </div>
                        </div>
                    </div>
                    <div className='line'>
                        <Total
                            title={'Result'}
                            subtitle={'Rate:'}
                            total={total}
                        />
                    </div>
        </section>
    );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>


Ответы (1 шт):

Автор решения: Daniil Loban

Если вкратце то значение нужно вытягивать из стейта isActive по статусу. Я создал дополнительную функцию calcTotal которая и занимается расчетами.

По-хорошему в React никаких вычислений не должно болтаться между функциями.

В стейте желательно держать только то что меняется в процессе работы компонента, либо приходит через API запросы.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Add React in One Minute</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin ></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin ></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
    <!-- Load our React component. -->
    <script type="text/babel">
      window.useState = React.useState;
      window.useEffect = React.useEffect
      
      const RangeInput =({id, onChange })=>
      <input onChange={(event) => onChange(id, Math.floor(event.target.value * 100)/100 )} type="range"/>
      
      const Btn =({ratio, id, onChange, checked, children})=><label>
        <input type="radio" onChange={()=>onChange(id)} value={ratio} name="ratio" checked={checked}/>{children}
      </label>
      
      const Total =({title,subtitle,total})=>(
        <div>
          <p>title={'Result'}</p>
          <p>subtitle={'Rate:'}</p>
          <p>total={total}</p>
        </div> 
      )
      function Component() {
          const [total, setTotal] = useState(0)
          const [ranges, setRanges] = useState([
              {
                  id: 'height',
                  min: 50,
                  max: 220,
                  label: 'height (sm)',
                  lng: 'lng-height',
                  value: 135,
              },
              {
                  id: 'weight',
                  min: 1,
                  max: 200,
                  label: 'weight (kg)',
                  lng: 'lng-weight',
                  value: 100,
              },
              { id: 'age', min: 1, max: 110, label: 'age', lng: 'lng-age', value: 55 },
          ]);

          const [isActive, setActive] = useState([
              { id: 'a0', status: true, name: 'low', ratio: 1.2 },
              { id: 'a1', status: false, name: 'small', ratio: 1.375 },
              { id: 'a2', status: false, name: 'medium', ratio: 1.55 },
              { id: 'a3', status: false, name: 'high', ratio: 1.725 },
          ]);

          useEffect(()=>{
              const ratio =  isActive.find(e => e.status === true).ratio;
              console.log(ratio)   
              const height = ranges[0].value;
              const weight = ranges[1].value;
              const age = ranges[2].value;
              setTotal((height + weight - age) * ratio);
          }, [isActive, ranges ])

          const chooseRange = (id, value) => {
              setRanges(
                  ranges.map((item) => (item.id === id ? { ...item, value } : item))
              );
          };

          const chooseActive = (id) => {
            setActive(
                isActive.map((item) =>
                    item.id === id ? { ...item, status: true } : { ...item, status: false }
                )
            );
          };


          return (
              <section id='component'>
                <div className='line line2'>
                    <div className='ranges df'>
                        {ranges.map((item) => (
                            <RangeInput
                                key={item.id}
                                onChange={chooseRange}
                                value={item.value}
                                {...item}
                            />
                        ))}
                    </div>
                </div>
                <div className='line line3'>
                    <div className='btns df fww'>
                        {isActive.map((item) => (
                            <Btn
                                key={item.id}
                                onChange={chooseActive}
                                id={item.id}
                                ratio={item.ratio}
                                checked={item.status ? "checked": ""}
                                className={
                                    item.status
                                        ? `btn lng-${item.name} active`
                                        : `btn lng-${item.name}`
                                }>
                                {item.name}
                            </Btn>
                        ))}
                    </div>
                </div>
            {/*</div>*/}
            <div className='line'>
                <Total
                    title={'Result'}
                    subtitle={'Rate:'}
                    total={total}
                />
            </div>
              </section>
          );
      }


      const App = () => {
        return (
          <div>
            <Component/>
          </div>
        );
      };

      ReactDOM.render(<App />, document.getElementById("root"));
    </script>
  </body>
</html>
 

→ Ссылка