Home > Software engineering >  Why does console log inside setState function when called from child component gets access to update
Why does console log inside setState function when called from child component gets access to update

Time:08-14

Below are the two mentioned cases when console logging a state results in different outputs.

CASE1: Console logging currentTitle state logs the previous state even after updating state in titleChangeHandler function.

See Case1 Console Log

import "./ExpenseForm.css";

const ExpenseForm = (props) => {

  const [currentTitle, setCurrentTitle] = useState("");
  const [currentAmount, setCurrentAmount] = useState("");
  const [currentDate, setCurrentDate] = useState("");

  const titleChangeHandler = (event) => {
    setCurrentTitle(event.target.value);
    console.log(currentTitle);
  };

  const amountChangeHandler = (event) => {
    setCurrentAmount(event.target.value);
 
  };

  const dateChangeHandler = (event) => {
    
    setCurrentDate(event.target.value);
  
  };

  const submitHandler = (event) => {
    event.preventDefault();

    const expenseData = {
      title: currentTitle,
      amount: currentAmount,
      date: currentDate,
    };

    props.onSaveExpenseData(expenseData);

    setCurrentAmount("");
    setCurrentTitle("");
    setCurrentDate("");
  };

  return (
    <form onSubmit={submitHandler}>
      <div className="new-expense__controls">
        <div className="new-expense__control">
          <label>Title</label>
          <input
            type="text"
            value={currentTitle}
            onChange={titleChangeHandler}
          />
        </div>
        <div className="new-expense__control">
          <label>Amount</label>
          <input
            type="number"
            min="0.1"
            step="0.1"
            value={currentAmount}
            onChange={amountChangeHandler}
          />
        </div>
        <div className="new-expense__control">
          <label>Date</label>
          <input
            type="date"
            min="2019-01-01"
            max="2022-12-31"
            value={currentDate}
            onChange={dateChangeHandler}
          />
        </div>
      </div>
      <div className="new-expense__actions">
        <button type="submit">Add expense</button>
      </div>
    </form>
  );
};

export default ExpenseForm;

CASE 2: When we pass a function(onSelectYear) to a child component using props, and call setState function(setYear) inside the passed function, when invoked from child component(ExpensesFilter), then console logging the state shows the latest value after updating. See Case2 Console Log

// Expenses.js 
import "./Expenses.css";
import ExpenseItem from "./ExpenseItem";
import React, { useState } from "react";
import ExpensesFilter from "./ExpenseFilter";
import Card from "../UI/Card";

const Expenses = (props) => {
  const [year, setYear] = useState("2020");
  const onSelectYear = (year) => {
    setYear(year);
    console.log(year);
  };
  return (
    <div>
      <Card className="expenses">
        <ExpensesFilter
          selectedYear={year}
          onSelectYear={onSelectYear}
        ></ExpensesFilter>
        <ExpenseItem
          title={props.expenses[0].title}
          amount={props.expenses[0].amount}
          date={props.expenses[0].date}
        ></ExpenseItem>
        <ExpenseItem
          title={props.expenses[1].title}
          amount={props.expenses[1].amount}
          date={props.expenses[1].date}
        ></ExpenseItem>
        <ExpenseItem
          title={props.expenses[2].title}
          amount={props.expenses[2].amount}
          date={props.expenses[2].date}
        ></ExpenseItem>
        <ExpenseItem
          title={props.expenses[3].title}
          amount={props.expenses[3].amount}
          date={props.expenses[3].date}
        ></ExpenseItem>
      </Card>
    </div>
  );
};

export default Expenses;

// ExpenseFilter.js
import React from "react";

import "./ExpenseFilter.css";

const ExpensesFilter = (props) => {
  const onSelectChange = (event) => {
    props.onSelectYear(event.target.value);
  };
  return (
    <div className="expenses-filter">
      <div className="expenses-filter__control">
        <label>Filter by year</label>
        <select value={props.selectedYear} onChange={onSelectChange}>
          <option value="2022">2022</option>
          <option value="2021">2021</option>
          <option value="2020">2020</option>
          <option value="2019">2019</option>
        </select>
      </div>
    </div>
  );
};

export default ExpensesFilter;


I understand that react schedules the state changes and this operation is async in nature. Thats why an immediate console log doesn't reflect updated value. But in the second case, we are essentially doing the same. Can anyone please explain, what makes the second case different from first?

CodePudding user response:

If we look at this part of the code:

const [year, setYear] = useState("2020");
const onSelectYear = (year) => {
  setYear(year);
  console.log(year);
};

The year in console.log(year) is the value that is being passed as an argument to the function onSelectYear

const [year /* this is out of scope for onSelectYear */ , setYear] = useState("2020");
const onSelectYear = (year /* this value */) => {
  setYear(year);
  console.log(year); // is being logged here
};

On changing the function definition like so:

const [year /* this is now in scope for onSelectYear */ , setYear] = useState("2020");
const onSelectYear = (newYear /* changed the name */) => {
  setYear(newYear);
  console.log(year); // the year as defined by useState is now being logged here
};

The same observation will be seen as Case 1.

  • Related