Home > Enterprise >  ReactJS code structure and rendering component
ReactJS code structure and rendering component

Time:12-09

I'm doing my first steps with ReactJS by trying to develop a small powerlifting application: the user enters the weight and the plates to use are displayed on the page.

I split my code like this:

  • a "Plate" component:
// props :
// - color (Red, blue, yellow, green, white, silver)
// - weight(25 or 2.5, 20, 15, 10, 5, 1.25)
function Plate(props){
    return <div className={`Plate ${props.color}Plate`}>
        <p>{props.weight}</p>
    </div>
    
}


export {Plate};
  • a "WeightInput" component:
import React, {Component} from "react"
import { Plate } from '../Plate/Plate';
import * as ReactDOM from 'react-dom/client'
class WeightInputForm extends Component{

    calculation = (weight) => {
        if(weight === 20) {
            console.log("empty bar");
            return;
        }
        const possibleWeights = [25, 20, 15, 10, 5, 2.5, 1.25];
        const orderedPlateColor = ["Red", "Blue", "Yellow", "Green", "White", "LittleRed", "Silver"];
        let plates = [];
        let sideWeight = (weight-20-5)/2;
        let plateNumber = 0;
        possibleWeights.forEach((currentWeight, index)=>{
            while(currentWeight <= sideWeight && sideWeight % currentWeight != sideWeight){
                plates.push(<Plate key={plateNumber  } color={orderedPlateColor[index]} weight={currentWeight}></Plate>);
                sideWeight -= currentWeight;
            }

            
        });
        return plates;
    }

    handleSubmit = (event) =>{
        event.preventDefault();
        const weightSubmitted = event.target[0].value;
        if(weightSubmitted === "") return;
        const platesRoot = ReactDOM.createRoot(document.getElementsByClassName("Plates")[0]);
        const plates = this.calculation(weightSubmitted);
        platesRoot.render(plates);
    }


    render() {
        return (<form onSubmit={this.handleSubmit}>
            <input type="number" step="2.5" min={20}/>
            <input type="submit" value="Submit"/>
        </form>);
    }
}

export default WeightInputForm;
  • here is the app file :
import './App.css';
import WeightInputForm from './components/WeightInput/WeightInput';

function App() {
  return (
    <div className="App">
      <h1>How to load your powerlifting bar ?</h1>
      <div className="Plates">
      </div>
      <div className='InputWeight'>
        <WeightInputForm></WeightInputForm>
      </div>
    </div>
  );
}

export default App;

Currently my application works as I want, however I have this warning in the console:

You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it.

I also tried not to use createRoot but only ReactDOM.render, obviously it is deprecated since React 17.

I have the impression that the way I qplir my application is not the right one.

Would you have any advice to remove this warning and make my code cleaner? Thanks in advance.

CodePudding user response:

You're kind of misunderstanding many things. With React the goal should be to rid of every direct contact with de DOM. document.get... should not be used. Instead use react hooks such as useState to dynamically display HTML contain. Make use of JSX syntax. Read more on React before getting started.

In most react app render is done once and only in the index.js (root file).

CodePudding user response:

There's quite a lot going on here which reflects a slightly flawed - but salvageable - understanding of how React works. Probably the key issue is how to utilise state to ensure that components properly update/reflect state changes, but I shall start at the beginning.

  1. A minor issue is that you're mixing function components and class components. I would stick with one or the other, particularly if you're writing all the code yourself.

  2. You should import Plate into App because that's presumably where it's going to be used. You should export it as export default Plate; just like the other components, and then import Plate from './Plate/plate';.

  3. For this app you should be using state management. There are lots of ways to approach this but the useState hook would be the easier here. Since the output of Plate relies on the value of Input and both of those components are in App the state should be held in App.

  4. To manage the state: create a function/handler in App that updates the state using the value from Input. Pass down that handler to the Input component, and that can call it whenever the input value changes.

  5. As you can see in the abbreviated example (which just shows the main concepts) Plate accepts the weight state as as property. When the state changes those changes are passed down to `Plate so it can render that value.

In summary it maybe a good idea for you to visit/revisit how React actually works, and take a look at some tutorials. Understanding how React components and state work together is rather pivotal to development with the library, and it's an important step to grasp before you can build an application properly.

Here's the example.

App contains both the Plate and Input components. It has state (weight) and a way to update that state (setWeight). It also has a handler (handleChange). It passes that handler down to Input which calls it when the change event fires on the input element. When that handler is called it updates the state with the input's value.

The weight state is passed down to the Plate component. When that state changes those changes are reflected in the re-rendered component.

const { useState } = React;

// Plate accepts a weight state value
// and renders it
function Plate({ weight }) {
  return <div>Plate: {weight}</div>;
}

// Input accepts the weight state value (the component
// is what is known as a controlled component), and the
// change handler. When the input element fires an event
// it calls that handler
function Input({ weight, handleChange }) {
  return (
    <input
      type="number"
      step="2.5"
      min="20"
      value={weight}
      onChange={handleChange}
    />
  );
}

function Example() {

  // Initialises state with `useState`
  const [ weight, setWeight ] = useState(0);

  // The handler - it accepts the event from the
  // element that fired it, and then grabs the value
  // from the element, and then updates the state with it
  function handleChange(e) {
    setWeight(e.target.value);
  }

  // The main component contains both the Plate
  // component, and the Input component, passing down
  // the relevant state, and handler to each
  return (
    <main>
      <Plate weight={weight} />
      <Input
        weight={weight}
        handleChange={handleChange}
      />
    </main>
  );

}

ReactDOM.render(
  <Example />,
  document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

  • Related