0

I've made a react component for reset password page. After a reset token is sent to the user email, the reset page has three inputs: token, new password and confirm password. The latter two are hidden until a sha256 token is entered in the token field, upon which the password fields appear and the token field disappears. This used to work until I also forwarded a URL with the reset token in the reset URL. Thus I had to add the compShow() function in my useEffect() as well so as soon as the component is loaded it checks for the token and updates the token field, rendering it invisible. This works in the URL with reset token but the URL without the reset which is supposed to show only token field first and then hide token and show password fields does not work as intended now. The token field only disappears if I press an extra character after the token is entered( I use space). I figure it is because the first time I change the value of the placeholder state variable in the onChangedHandler function, compShow() does not get triggered. But when I add an extra character, the compShow function detects the change in the placeholder and executes its respective code.

Can someone tell me why does this happens and what should I do to get the intended result?

The code snippet is provided below

const [placeholder, setPlaceholder] = useState('')

const { onReleaseError, onError } = props

const compShow = useCallback(() => {
    if (validator.isHash(placeholder, 'sha256')) {
      setShowToken({ display: 'none' })
      setShow(style.show)
      setErrorType('red')
      onReleaseError()
    }
  }, [placeholder, onReleaseError])

  useEffect(() => {
    const path = new URL(document.location).pathname.split('/')[2] || null
    if (path) {
      setPlaceholder(path)
      compShow()
    } else {
      setErr(onError)
      if (onError) setErrorType('green')
    }
  }, [compShow, onError])

  const onChangeHandler = e => {
    setPlaceholder(e.target.value)
    compShow()
  }
  • 2
    This question has already been answered a few times. Please refer to this: https://stackoverflow.com/questions/53845595/wrong-react-hooks-behaviour-with-event-listener – Thanh-Quy Nguyen Apr 12 '20 at 15:26
  • half of the variables are undefined here, can you please create a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)? – Aprillion Apr 12 '20 at 15:32
  • 1
    the only thing I can say that `compShow` does not change simply by being called. on the other hand, if `onError` is a function created in parent component without `useCallback`, it will change every time the parent is re-rendered, triggering the useEffect. also, `compShow` will change every time that `placeholder` is changed, so there is a potential infinite loop between dependencies.. – Aprillion Apr 12 '20 at 15:38
  • @Aprillion `onError` is a redux state variable altered in the previous( sibling) component simply to notify the token is sent to the mail and supposed to be nullified as soon as a valid reset token is entered. It possibly would re-render `useEffect()` after its value changes. Any ideas to get out of it? – Aakash Meshram Apr 12 '20 at 15:51
  • @Thanh-QuyNguyen thanks mahn. Will look into useRefs. – Aakash Meshram Apr 12 '20 at 15:55

1 Answers1

1

Apparently the solution was much simpler. useCallback locks in the value it takes at the begining of a component render/ update. The placeholder defined at the beginning of the component is an empty string, hence it does not change while we call the compShow function. But as I take in an input which may or may not be placeholder but has the same value, compShow function takes the updated value of placeholder and functions as intended.

const [placeholder, setPlaceholder] = useState('')
const { onReleaseError, onError } = props

const compShow = useCallback(
    val => {
      if (validator.isHash(val, 'sha256')) {
        setShowToken({ display: 'none' })
        setShow(style.show)
        setErrorType('red')
        onReleaseError()
      }
    },
    [onReleaseError]
  )

  useEffect(() => {
    const path = new URL(document.location).pathname.split('/')[2] || null
    if (path) {
      setPlaceholder(path)
      compShow(path)
    } else {
      setErr(onError)
      if (onError) setErrorType('green')
    }
  }, [compShow, onError])

  const onChangeHandler = e => {
    setPlaceholder(e.target.value)
    compShow(e.target.value)
  }
  • 1
    nice solution, but please note the explanation has nothing to do with useCallback. placeholder is a constant value, calling setPlaceholder won't modify the variable, it will only trigger a new render and a different execution of the same function will create a different constant (also called placeholder) with the new value. the same value that is rendered is also used inside useCallback or other closures. if you need to use the future value instead of the current one, then your solution will work here perfectly – Aprillion Apr 15 '20 at 16:44