简体   繁体   中英

Function called inside a component doesn't have updated props

I have created a reproducible exam of my problem, I don't understand why after the setDefaultValue is called and the component is updated (you can see it's updated using the result of my console.log) If now I click on the reset button instead of the new defaultValue I see the old one.
Here is a link to the example showing this problem, I'll also paste the code here
https://codesandbox.io/s/wonderful-tree-wtsgb4?file=/src/App.js

import "./styles.css";

import {useState, useRef} from 'react';
import TextBox from './TextBox';

export default function App() {
  const textboxAPI = useRef(null)
  const [defaultValue ,setDefaultValue] = useState('First')
  return (
    <div className="App">
      <div style={{fontWeight: 'bold'}}>To reproduce please first click on the default value button then on the reset button</div>

      <TextBox getAPI={(api) => textboxAPI.current = api} defaultValue={defaultValue}/>
      <button onClick={() => setDefaultValue('second')}>1- Click me to change default value to "second"</button>
      <button onClick={() => textboxAPI.current.reset()}>2- Click me to call reset inside Textbox</button>
    </div>
  );
}

import {useEffect, useState} from 'react';

const TextBox = ({defaultValue, getAPI}) => {
  const [value, setValue] = useState(defaultValue || '')

  useEffect(() => {
    if (getAPI) {
      getAPI({
        reset: reset,
      })
    }
  }, [])

  const reset = () => {
    console.log('TextBox Reset DefaultValue', defaultValue)
    setValue(defaultValue)
  }

  console.log('TextBox DefaultValue', defaultValue)

  return <div>{value}</div>
}

export default TextBox;

To reproduce the problem:
1- Click on the first button to set a new defaultValue , see the console.log, you can see the defaultValue has changed inside the TextBox Component
2- Click the reset button, it calls the reset function inside TextBox but the default value logged there has the previous value!

Here you save in textboxAPI.current function reset but just one time after first render of TextBox component. Function reset has a defaultValue in a closure and its value is 'First' during first render. So each next time you call textboxAPI.current.reset() , you call the reset function with defaultValue==='First' in its closure.

But you parent component controls child state and React does not recommend to manage your logic like that.

[UPDATED] That will fix your issue, but I don not recommend to organize a state logic like that :

const TextBox = ({defaultValue, getAPI}) => {
  const [value, setValue] = useState(defaultValue || '')

  const reset = () => {
    console.log('TextBox Reset DefaultValue', defaultValue)
    setValue(defaultValue)
  }


  if (getAPI) {
    getAPI({
      reset: reset,
    })
  }

  console.log('TextBox DefaultValue', defaultValue)

  return <div>{value}</div>
}

export default TextBox;

Based on what I learned from the comments I tried using Hooks but There were too many changes needed especially I had some issues with React.lazy so I tried to look for more solutions until I found that using a combination of forwardRef and useImperativeHandle I can export my reset function without changing my current structure and it works as it should, I thought that I should share my solution for anyone else who might be looking for an alternative solution to Hooks .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM