2

I create app that render inputs dynamically, some result of calculations I need to set to every block to spans className="strongCrist" and className="strongRuby". But sets only same value to all blocks spans. On pure JavaScript it was very simple - in loop we add innerHTML to span and add span by parentElement.appendChild.

const { useState, useEffect } = React;

function PrimaryInputs() {
  const [playersQuantity, setPlayersQuantity] = useState(0);
  const [cristQuantity, setCristQuantity] = useState(0);
  const [rubyQuantity, setRubyQuantity] = useState(0);
  const [isCalculated, setIsCalculated] = useState(false);
  const [inputs, setInputs] = useState({});
  const [cupsQuantity, setCupsQuantity] = useState(0);
  const [dole, setDole] = useState(0);
  const [doleCrist, setDoleCrist] = useState(0);
  const [doleRuby, setDoleRuby] = useState(0);

  function playersHandleChange(event) {
    setPlayersQuantity(event.target.value);
  }
  function cristHandleChange(event) {
    setCristQuantity(event.target.value);
  }
  function rubyHandleChange(event) {
    setRubyQuantity(event.target.value);
  }
  function iterator(quantity) {
    let elems = [];
    for (var i = 0; i < quantity; i++) {
      elems.push(
        <div
          className={`playersBlock ${isCalculated ? "calculated" : ""} `}
          key={`player${i}`}
        >
          <input
            type="number"
            name={`cupsOf${i}`}
            placeholder="players cups"
            className="plCup"
            onChange={cupsHandleChange}
          />
          <br />
          <span className="strongCrist">Cristalls: {doleCrist}</span>
          <br />
          <span className="strongRuby">Rubies: {doleRuby}</span>
        </div>
      );
    }
    return elems;
  }
  function cupsHandleChange(event) {
    const { name, value } = event.target;
    setInputs((prev) => {
      return { ...prev, [name]: Number(value) };
    });
    setIsCalculated(true);
  }

  function countCups() {
    let i = 0;
    for (const key in inputs) {
      const val = inputs[key];
      if (val) {
        i += val;
      }
      setCupsQuantity(i);
    }
  }

  function countDole() {
    for (const key in inputs) {
      const val = inputs[key];
      if (val) {
        setDole(val / cupsQuantity);
        setDoleCrist(Math.round(dole * cristQuantity));
        setDoleRuby(Math.round(dole * rubyQuantity));
      }
    }
  }

  useEffect(() => {
    countCups();
    countDole();
  });

  return (
    <>
      <div className="mainInputs">
        <div className="input_wrapper">
          <label htmlFor="players">Enter quantity of players</label>
          <input
            type="number"
            name="players"
            id="players"
            onChange={playersHandleChange}
          />
        </div>
        <div className="input_wrapper">
          <label htmlFor="cristall">Enter cristalls</label>
          <input
            type="number"
            name="cristall"
            id="allCrist"
            onChange={cristHandleChange}
          />
        </div>
        <div className="input_wrapper">
          <label htmlFor="ruby">Enter rubies</label>
          <input
            type="number"
            name="ruby"
            id="allRuby"
            onChange={rubyHandleChange}
          />
        </div>
      </div>
      <hr />
      <span>
        {playersQuantity ? `players quantity: ${playersQuantity}` : null}
      </span>
      <br />
      <span className="sumCup">
        {cupsQuantity ? `cups quantity: ${cupsQuantity}` : null}
      </span>
      <div id="playersList">{iterator(playersQuantity)}</div>
    </>
  );
}

ReactDOM.render(
  <>
    <PrimaryInputs />
  </>,
  document.getElementById("mydiv")
);

1 Answer 1

1

I think this is the behaviour you need, the main thing was to create a separate component for the player, to allow it to have it's own state:

import React, { useEffect, useRef, useState } from 'react';

const int = (value) => {
    let integer = parseInt(value);
    if (isNaN(integer) || integer < 0) integer = 0;
    return integer;
}

//new component player block, to allow each one have it's own value
const PlayerBlock = ({ cristQuantity, rubyQuantity, setTotalCupsQuantity }) => {
    const [doleRuby, setDoleRuby] = useState();
    const [doleCrist, setDoleCrist] = useState();
    const [playerCups, setPlayerCups] = useState(0);


    //ref to access the input
    const ref = useRef();

    //useEffect that will trigger when the parent component's cristQuantity and rubyQuantity change, this is necessary to update the player ruby and cris when they are changed
    useEffect(() => {
        setDoleRuby(int(ref.current.value) * rubyQuantity);
        setDoleCrist(int(ref.current.value) * cristQuantity);
    }, [cristQuantity, rubyQuantity])

    function cupsHandleChange(event) {
        const inputVal = int(event.target.value);

        console.log("inputVal", inputVal);

        setPlayerCups(inputVal);
        setTotalCupsQuantity((previousValue) => {
            previousValue -= playerCups;
            return previousValue + inputVal;
        })
        setDoleRuby(inputVal * rubyQuantity);
        setDoleCrist(inputVal * cristQuantity);
    }

    return <div className={`playersBlock calculated`}>
        <input
            type="number"
            placeholder="players cups"
            className="plCup"
            ref={ref}
            onChange={cupsHandleChange}
        />
        <br />
        <span className="strongCrist">Cristalls: {doleCrist}</span>
        <br />
        <span className="strongRuby">Rubies: {doleRuby}</span>
    </div>
}

export default function PrimaryInputs() {
    const [playersQuantity, setPlayersQuantity] = useState(0);
    const [cristQuantity, setCristQuantity] = useState(0);
    const [rubyQuantity, setRubyQuantity] = useState(0);
    const [totalCupsQuantity, setTotalCupsQuantity] = useState(0);

    function playersHandleChange(event) {
        setPlayersQuantity(int(event.target.value));
    }

    function cristHandleChange(event) {
        setCristQuantity(int(event.target.value));
    }

    function rubyHandleChange(event) {
        setRubyQuantity(int(event.target.value));
    }

    return (
        <>
            <div className="mainInputs">
                <div className="input_wrapper">
                    <label htmlFor="players">Enter quantity of players</label>
                    <input
                        type="number"
                        name="players"
                        id="players"
                        onChange={playersHandleChange}
                    />
                </div>
                <div className="input_wrapper">
                    <label htmlFor="cristall">Enter cristalls</label>
                    <input
                        type="number"
                        name="cristall"
                        id="allCrist"
                        onChange={cristHandleChange}
                    />
                </div>
                <div className="input_wrapper">
                    <label htmlFor="ruby">Enter rubies</label>
                    <input
                        type="number"
                        name="ruby"
                        id="allRuby"
                        onChange={rubyHandleChange}
                    />
                </div>
            </div>
            <hr />
            <span>
                {playersQuantity ? `players quantity: ${playersQuantity}` : null}
            </span>
            <br />
            <span className="sumCup">
                {totalCupsQuantity ? `cups quantity: ${totalCupsQuantity}` : null}
            </span>
            <div id="playersList">
                {new Array(playersQuantity).fill().map((el, i) => { //inline way to loop, creates a dummy array with the size of playerquantity, and just loops through it rendering the new component, and passing in the necessary
                    return <PlayerBlock
                        key={`player${i}`}
                        cristQuantity={cristQuantity}
                        rubyQuantity={rubyQuantity}
                        setTotalCupsQuantity={setTotalCupsQuantity} />
                }
                )}
            </div>
        </>
    );
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.