skip to Main Content

I translate component to hook code and I have difference between both.
(note that the Refresher is callback the fetchApi to refresh data)

export default class myComponent extends Component {
    constructor(props) {
        super(props);
        this.state = { test: false };
    }

    componentDidMount() {
        this.fetchApi();
    }

    fetchApi = () => {
        console.log("fetchApi: " + this.state.test);

        this.setState({ test: !this.state.test }, () => console.log("useEffect: " + this.state.test));
    }

    render() {
        return <div>
            <label>{"test: " + this.state.test}</label>
            <Refresher refresh={this.fetchApi.bind(this)} />
        </div>
    }
}

On my screen, test is switching true to false and false to true at each refresh.

export default function myHook(props) {
    const [test, setTest] = useState(false);

    let TEST = false;

    useEffect(() => fetchApi(), []);
    useEffect(() => console.log("useEffect: " + test), [test]);

    const fetchApi = () => {
        console.log("fetchApi: test = " + test);
        console.log("fetchApi: TEST = " + TEST);

        setTest(!test);
        TEST = !TEST;
    }

    return <div>
        <label>{"test: " + test}</label>
        <Refresher refresh={fetchApi} />
    </div>
}

On my screen, test is staying on true value

On console.

Component:

fetchApi: false
useEffect: true
fetchApi: true
useEffect: false
fetchApi: false
useEffect: true

Hook:

fetchApi: test = false
fetchApi: TEST = false
useEffect: false
useEffect: true
fetchApi: test = false
fetchApi: TEST = true
fetchApi: test = false
fetchApi: TEST = false

Why my test variable is it always to false value?

2

Answers


  1. The class components and function components with hooks are not exactly the same, but they are equivalent in most cases.

    Your test variable is always false in the hook code because you are using the stale state value in the fetchApi function. When you call setTest, you are not updating the test variable immediately, but scheduling a state update for the next render. However, the fetchApi function still has a closure over the old test value – false.

    To fix this, you can use a functional update for setTest, which will use the latest state value.

    For example:

    const fetchApi = () => {
      console.log("fetchApi: test = " + test);
    
      setTest((prevTest) => {
        const newTest = !prevTest;
        console.log("fetchApi: newTest = " + newTest);
        return newTest;
      });
    };
    
    Login or Signup to reply.
  2. I think you have some mistake in logging.
    Inside functional component you have a variable:

    let TEST = false;
    

    That variable will be created each render with value false and inside fetchApi (which is also recreated each render) variable TEST always will be false. The TEST variable update to true will not be kept as new variable is created each render. But you have following logging:

    fetchApi: test = false
    fetchApi: TEST = true
    fetchApi: test = false
    fetchApi: TEST = false
    

    where variable TEST has different values. Imho in logging there is a mistake (typo or whatever) and test should true/false and TEST should be always false.

    Codesandbox snippet of your example – https://codesandbox.io/s/test-example-lv2qks
    And it seems all work as expected.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search