facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
227.4k stars 46.38k forks source link

useState doesn't update component #14794

Closed ThinkSalat closed 5 years ago

ThinkSalat commented 5 years ago

Here is my code. Clicking the buttons never updates the component. It does change the variable page but the component itself never re renders or shows the current value of the variable.

react and react-dom are version 16.8.1 babel-core is 6.26.0

const AssessmentWizard = (props) => {
  let [page, setPage] = useState(1)

  return (
    <div>
      {page}
      <Button onClick={() => setPage(page--)}>Previous Page</Button>
      <Button onClick={() => setPage(page++)}>Next Page</Button>
    </div>
  )
}

additionally, adding the example counter from the intro to hooks doc also didn't work. I could tell I was clicking a button, but it never changed the view.

anhphamduy commented 5 years ago

page++ inside setPage means "after setting state, increase page by 1". The fix is to use ++page because it'd increase page by 1 and then set state.

In your case, you should be using neither of them as state should only be mutated by setPage. For example, when doing page-- or --page, under the hood, they're actually page = page - 1.

ThinkSalat commented 5 years ago

page++ inside setPage means "after setting state, increase page by 1". The fix is to use ++page because it'd increase page by 1 and then set state.

In your case, you should be using neither of them as state should only be mutated by setPage. For example, when doing page-- or --page, under the hood, they're actually page = page - 1.

I get that but that's not why the component is not updating. I literally copied and pasted the example from the intro to hooks page, and it also doesn't update either.

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

When I switch to setPage(page + 1), and then console log page, it stays at one no matter how many times I click it.

hamlim commented 5 years ago

@ThinkSalat Do you have a codesandbox or jsfiddle link recreating this issue?

gaearon commented 5 years ago

Needs a reproducing fiddle, thanks

ThinkSalat commented 5 years ago

I can't get it to reproduce. I think it might have something to do with one of the dependencies. are react-redux 5.1.1, webpack 3.8.1, and babel 6.2.6 compatible with this version?

gaearon commented 5 years ago

Should be. Maybe you have some dev-only thing that messes it up.

tgochenour commented 5 years ago

My useState() hook no longer works with the latest version of react just released. I have been working with react@16.7.0-alpha.2 and just attempted an update to react@16.8.1.

When I did this a useState hook in my code stopped working. When I re-installed the alpha version the useState hook begins to work once again.

export default function Field(props) {
    const data = useContext(DataContext);
    let [update, setUpdate] = useState(0);
    function render() {
        console.log('   ***   Field.setUpdate()'); 
        setUpdate(update+1);
    }
...
}

With the latest version of react, a break on the line calling setUpdate() the value for update is always 0. With the alpha version the update value increments by 1 every time render() is called.

MrCrimp commented 5 years ago

I had the exact same issue and solved it by updating react-hot-loader from 4.3.4 => 4.6.5.

ThinkSalat commented 5 years ago

I had the exact same issue and solved it by updating react-hot-loader from 4.3.4 => 4.6.5.

I gave that a try and am having the same issue. @gaearon, do you have any advice on chasing down the issue? How can I debug this?

Here are my dependencies from package.json

"dependencies": { "@material-ui/codemod": "^1.1.0", "@material-ui/core": "^1.5.1", "@material-ui/icons": "^2.0.3", "antd": "^3.4.1", "apexcharts": "^2.5.1", "autoprefixer": "7.1.6", "autosuggest-highlight": "^3.1.1", "axios": "^0.18.0", "babel-core": "^6.26.0", "babel-eslint": "^7.2.3", "babel-jest": "20.0.3", "babel-loader": "7.1.2", "babel-plugin-transform-remove-console": "^6.9.4", "babel-preset-react-app": "^3.1.1", "babel-runtime": "6.26.0", "c3": "^0.6.12", "case-sensitive-paths-webpack-plugin": "2.1.1", "chalk": "1.1.3", "change-case": "^3.1.0", "chart.js": "^2.7.3", "classnames": "^2.2.6", "css-loader": "0.28.7", "dotenv": "4.0.0", "dotenv-expand": "4.2.0", "downshift": "^2.0.4", "eslint": "^5.8.0", "eslint-config-react-app": "^2.1.0", "eslint-loader": "1.9.0", "eslint-plugin-flowtype": "2.39.1", "eslint-plugin-import": "^2.14.0", "eslint-plugin-jsx-a11y": "^6.1.2", "eslint-plugin-react": "^7.11.1", "extract-text-webpack-plugin": "3.0.2", "file-loader": "1.1.5", "fs-extra": "3.0.1", "html-webpack-plugin": "2.29.0", "html2canvas": "^1.0.0-alpha.12", "jest": "20.0.4", "jspdf": "^1.4.0", "lodash": "^4.17.10", "material-design-icons": "^3.0.1", "mixpanel-browser": "^2.26.0", "moment": "^2.22.1", "moment-timezone": "^0.5.23", "object-assign": "4.1.1", "owasp-password-strength-test": "^1.3.0", "postcss-flexbugs-fixes": "3.2.0", "postcss-loader": "2.0.8", "promise": "8.0.1", "raf": "3.4.0", "react": "^16.8.1", "react-apexcharts": "^1.2.1", "react-autosuggest": "^9.3.4", "react-c3js": "^0.1.20", "react-chartkick": "^0.3.0", "react-dev-utils": "^5.0.1", "react-dimensions": "^1.3.1", "react-dom": "^16.8.1", "react-draggable": "^3.1.1", "react-easy-chart": "^1.0.0", "react-fontawesome": "^1.6.1", "react-idle-timer": "^4.0.9", "react-password-strength": "^2.3.1", "react-redux": "^5.1.1", "react-responsive": "^5.0.0", "react-router-dom": "^4.2.2", "react-router-redux": "^5.0.0-alpha.9", "react-sticky": "^6.0.2", "react-virtualized": "^9.21.0", "react-widgets": "^4.2.6", "recompose": "^0.27.1", "redux": "^3.7.2", "redux-devtools-extension": "^2.13.5", "redux-form": "^7.3.0", "redux-persist": "^5.9.1", "redux-saga": "^0.16.0", "reselect": "^4.0.0", "resolve": "1.6.0", "style-loader": "0.19.0", "sw-precache-webpack-plugin": "0.11.4", "url-loader": "0.6.2", "webpack": "3.8.1", "webpack-dev-server": "2.9.4", "webpack-manifest-plugin": "1.3.2", "whatwg-fetch": "2.0.3", "why-did-you-update": "^0.1.1", "zxcvbn": "^4.4.2" }, "scripts": { "start": "PORT=3001 node scripts/start.js", "build": "node scripts/build.js", "test": "node scripts/test.js --env=jsdom" }, "jest": { "collectCoverageFrom": [ "src//.{js,jsx,mjs}" ], "setupFiles": [ "/config/polyfills.js" ], "testMatch": [ "/src//tests//.{js,jsx,mjs}", "/src//?(.)(spec|test).{js,jsx,mjs}" ], "testEnvironment": "node", "testURL": "http://localhost", "transform": { "^.+\.(js|jsx|mjs)$": "/node_modules/babel-jest", "^.+\.css$": "/config/jest/cssTransform.js", "^(?!.\.(js|jsx|mjs|css|json)$)": "/config/jest/fileTransform.js" }, "transformIgnorePatterns": [ "[/\\]node_modules[/\\].+\.(js|jsx|mjs)$" ], "moduleNameMapper": { "^react-native$": "react-native-web" }, "moduleFileExtensions": [ "web.js", "js", "json", "web.jsx", "jsx", "node", "mjs" ] }, "babel": { "presets": [ "react-app" ] }, "eslintConfig": { "extends": "react-app" }, "devDependencies": { "eslint-plugin-react-hooks": "^1.0.1", "react-hot-loader": "^4.6.5" }

ThinkSalat commented 5 years ago

I also randomly get the error: Hooks can only be called inside the body of a function component. (https://fb.me/react-invalid-hook-call)

This is being called on the example from the docs which I shared in a comment above. It resolves when I refresh the page.

Here's a picture of the error: screen shot 2019-02-12 at 11 08 44 am

ThinkSalat commented 5 years ago

I believe this does have to do with React-Hot-Loader.

I replicated it by using create-react-app, installing react-hot-loader@4.3.11, adding "plugins": ["react-hot-loader/babel"] to .babelrc

Here's the full app.js:

import React, { Component, useState } from 'react';
import logo from './logo.svg';
import './App.css';
import { hot } from 'react-hot-loader'

function Example() {
  // Declare a new state variable, which we'll call "count"
  const countArr = useState(0);
  let count = countArr[0]
  let setCount = countArr[1]

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

class App extends Component {
  render() {
    return (
      <div className="App">
      <Example />
      </div>
    );
  }
}

export default hot(module)(App);

This replicates the issues of the hook not working.

tgochenour commented 5 years ago

I do not see a reference to react-hot-loader as a dependency in package.json.

On further testing I see that in both cases (react@16.7.0-alpha.2 and 16.8.1) when I break on the line setUpdate the value for update is always zero. It does not increment in the alpha version as I said before. In the alpha version, the call to setUpdate(update+1) triggers a rendering of the component, whereas with 16.8.1 is does not.

Evidently there is a scoping issue in the declaration of the render() function which causes the closure to be early bound to the initial value of zero and not late bound to the current value as I would hope.

export default function Field(props) {
    const data = useContext(DataContext);
    let [update, setUpdate] = useState(0);
    function render() {
        console.log('   ***   Field.setUpdate()'); 
        setUpdate(update+1);
    }
...
}
gaearon commented 5 years ago

I'm closing this issue because the original report has no reproduction, and further reports mention tools like react-hot-loader. The current implementation of react-hot-loader is very invasive and I’m not surprised if it breaks something. I’d recommend turning it off and trying without it. If you have some issues, please raise them in the react-hot-loader repository.

If you have an unrelated problem, please file a new one with a runnable reproducing example. Thanks.

gaearon commented 5 years ago

I will also lock because issues like this tend to attract misinformation and wrong workarounds. Again — if you can reproduce this reliably and don’t use experimental projects like react-hot-loader then please file a new issue with a reproducing case. Thanks.