Search code examples
reactjsreact-hooksuse-effectuse-state

Why won't react modal input value populate when state is updated?


I have a modal that is triggered on a button click that needs to be populated with an input value that can be edited by the user. When the button is clicked, modal state is updated, but the input either:

a. doesn't reflect the updated state as its value (this happens when I use 'defaultValue' --or--

b. won't let the user edit the value in the input (this happens when I use 'value'.

How can I populate the input with the current state and also be able to edit it? Shouldn't changing the state that the input value is set to trigger a re-render of the modal?

function EditModal(props) {
    const [value, setValue] = useState(props.modalVal);
    const[hidden, setHidden] = useState(true)
    const[initialRender, setInitialRender] = useState(true)
    useEffect(()=>{
        setInitialRender(false)
    },[])
    useEffect(() => {
        setValue(props.modalVal)// <-- why doesn't this update input value?
    }, [props.modalVal]);
    useEffect(()=>{
        if(!initialRender){
            setHidden(false)
        }
    },[value])
    return (
        <Modal hidden={hidden} >
            <Text>Edit Item</Text>
            <form action="submit" onSubmit={props.submit}>
                <Input onChange={props.handleChange} defaultValue={value} />
                <Button>OK</Button>
            </form>
        </Modal>
    )
}

Solution

  • If you set the value using value then when the user edits it, you need to ensure that the onChange function you specify updates the value so that on the next render, the value is updated.

    It looks like you have a value (props.modalVal) that you should use as value (note that you do not need the useEffect or the useState for this, as changing props causes a re-render). What you therefore need to make sure is that props.handleChange changes modalValue

    Something like this:

    <Input onChange={props.handleChange} value={props.modalValue} />
    

    and in the parent component:

    const ParentComponent = ()=> {
      const [modalValue, setModalValue] = useState('initialValue');
    
      const handleChange = (event) => {
        setModalValue(event.currentTarget.value);
      };
    
      return (
        <EditModal handleChange={handleChange} modalValue={modalValue} />
      )
    }