skip to Main Content

I´m trying to add the value that the user inputs into the number of pages field, everything I´ve tried so far is not working. Here is the raw code without onChange for the inputs fields.

I´ve tried to use onChange without any luck, the value for the fields always remains the starting value, I´ve tried to change it through useState but no luck either.

Could anyone show me how to go about this?

import "./App.css";

function App(props) {
  const [total, setTotal] = useState(0);
  const [cost, setCost] = useState({
    website: false,
    seo: false,
    google: false,
    pages: 1,
    languages: 1,
    total: 0,
  });
  const [isWebChecked, setIsWebChecked] = useState(false);

  useEffect(() => {
    calculateTotal();
    console.log("useEffect up and running");
    console.log(cost.pages);
    setIsWebChecked(cost.website);
  }, [cost]);

  const handleOnChange = (event) => {
    let { name } = event.target;
    let newCost = { ...cost };
    newCost[name] = !newCost[name];

    setCost((prev) => (prev = newCost));
    if (cost.website === true) {
      setIsWebChecked(true);
    }
  };

  const calculateTotal = (event) => {
    let newTotal =
      0 +
      (cost.website && 500) +
      (cost.seo && 300) +
      (cost.google && 200) +
      ((cost.pages > 1 || cost.languages > 1) &&
        cost.pages * cost.languages * 30);
    console.log(cost.pages);
    setTotal((prev) => (prev = newTotal));
  };

  return (
    <div classname="App">
      <h3> Which services do you require?</h3>
      <p>
        <input
          type="checkbox"
          name="website"
          onChange={handleOnChange}
          value="500"
        />{" "}
        A website (500 €)
      </p>
      {isWebChecked && (
        <div className="modal">
          <label htmlFor="pages">Number of web pages </label>
          <input type="text" name="pages"></input>
          <br />
          <br />
          <label htmlFor="lang">Number of languages </label>
          <input type="number" name="languages"></input>
        </div>
      )}
      <p>
        <input
          type="checkbox"
          name="seo"
          onChange={handleOnChange}
          value="300"
        />{" "}
        A SEO consultancy (300 €)
      </p>
      <p>
        <input
          type="checkbox"
          name="google"
          onChange={handleOnChange}
          value="200"
        />{" "}
        A Google Ads Campaign (200 €)
      </p>

      <p>
        {" "}
        <strong>Total price: {total} €</strong>{" "}
      </p>
    </div>
  );
}

export default App;```

2

Answers


  1. There’s an example on this page called "Handling Forms":
    https://www.w3schools.com/react/react_forms.asp
    You will need to assign the state you are changing in your on change handler (handleOnChange for you if I see it right) to the value attribute of the input you want to change.

    So for instance for the Number of web pages field it would look something like:
    <input type="text" name="pages" onChange={handleNumOfPagesChange} value={numOfPages}> where numOfPages would be your state.
    Also I would avoid complicated objects as state (looking at cost state).

    Login or Signup to reply.
  2. We have this:

      const [cost, setCost] = useState({
        website: false,
        seo: false,
        google: false,
        pages: 1,
        languages: 1,
        total: 0,
      });
    

    Then we can update the cost.pages & cost.languages state whenever the input value change with this:

      <input
          type="text"
          name="pages"
          onChange={(e) => setCost({ ...cost, pages: e.target.value })}
      />
    
      <input
          type="number"
          name="languages"
          onChange={(e) => setCost({ ...cost, languages: e.target.value })}
      />
    

    Your calculateTotal have to use react useCallback hooks since the calculation depends on cost state.

      const calculateTotal = useCallback(
        (event) => {
          let newTotal =
            0 +
            (cost.website && 500) +
            (cost.seo && 300) +
            (cost.google && 200) +
            ((cost.pages > 1 || cost.languages > 1) &&
              cost.pages * cost.languages * 30);
          setTotal((prev) => (prev = newTotal));
        },
        [cost]
      );
    

    Create calculation trigger, where calculateTotal will be called on any cost changes:

      useEffect(() => {
        calculateTotal();
      }, [cost, calculateTotal]);
    

    And add useEffect watcher to reset both cost.pages & cost.languages when deselect cost.website:

      useEffect(() => {
        !cost.website &&
          (cost.pages > 1 || cost.languages > 1) &&
          setCost({ ...cost, pages: 1, languages: 1 });
      }, [cost]);
    

    And finaly, the working codes is as follows:

    function App() {
        const [total, setTotal] = React.useState(0);
        const [cost, setCost] = React.useState({
            website: false,
            seo: false,
            google: false,
            pages: 1,
            languages: 1,
            total: 0,
        });
        const [isWebChecked, setIsWebChecked] = React.useState(false);
    
        const calculateTotal = React.useCallback(
            (event) => {
            let newTotal =
                0 +
                (cost.website && 500) +
                (cost.seo && 300) +
                (cost.google && 200) +
                ((cost.pages > 1 || cost.languages > 1) &&
                cost.pages * cost.languages * 30);
            setTotal((prev) => (prev = newTotal));
            },
            [cost]
        );
    
        React.useEffect(() => {
            calculateTotal();
        }, [cost, calculateTotal]);
    
        const handleOnChange = React.useCallback(
            (event) => {
            const { name } = event.target;
            const newCost = { ...cost };
            newCost[name] = !newCost[name];
            setCost((prev) => newCost);
            setIsWebChecked(newCost.website);
            },
            [cost]
        );
    
        React.useEffect(() => {
            !cost.website &&
            (cost.pages > 1 || cost.languages > 1) &&
            setCost({ ...cost, pages: 1, languages: 1 });
        }, [cost]);
    
        return (
            <div className="App">
                <h3> Which services do you require?</h3>
                <p>
                    <input
                    type="checkbox"
                    name="website"
                    onChange={handleOnChange}
                    value="500"
                    />{" "}
                    A website (500 €)
                </p>
                {isWebChecked && (
                    <div className="modal">
                    <label htmlFor="pages">Number of web pages </label>
                    <input
                        type="text"
                        name="pages"
                        onChange={(e) => setCost({ ...cost, pages: e.target.value })}
                    />
                    <br />
                    <br />
                    <label htmlFor="lang">Number of languages </label>
                    <input
                        type="number"
                        name="languages"
                        onChange={(e) => setCost({ ...cost, languages: e.target.value })}
                    />
                    </div>
                )}
                <p>
                    <input
                    type="checkbox"
                    name="seo"
                    onChange={handleOnChange}
                    value="300"
                    />{" "}
                    A SEO consultancy (300 €)
                </p>
                <p>
                    <input
                    type="checkbox"
                    name="google"
                    onChange={handleOnChange}
                    value="200"
                    />{" "}
                    A Google Ads Campaign (200 €)
                </p>
    
                <p>
                    {" "}
                    <strong>Total price: {total} €</strong>{" "}
                </p>
            </div>
        );
    }
    
    ReactDOM.render(<App />, document.querySelector('.react'));
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <div class='react'></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search