简体   繁体   中英

In React, initially setState using value from props, and then update state using FormControl

Not sure how to make this into a reproducible example within stackoverflow, but we have the following React component that creates an SVG graph, that has an editable header text (can click on the text to edit and type to edit the text) leveraging foreignObject and FormControl

import { FormControl } from 'react-bootstrap';

function OurGraph({ headerText }) {
    const [graphText, setGraphText] = useState(headerText);

    // create editable header
    const ourHeader =
        (<foreignObject width='100%' height='40'>
            <div xmlns='http://www.w3.org/1999/xhtml'>
                <FormControl
                    className='modal-input no-style'
                    type='text'
                    value={graphText}
                    onChange={e => setGraphText(e.target.value)}
                    name='subheader'
                    placeholder=''
                />
            </div>
        </foreignObject>);

    // and return
    return (<svg>{ourHeader}</svg>);
}

and we have this parent component that updates the default headerText for the OurGraph component:

function Parent() {
    const [button, setButton] = useState(true);
    const buttonElement =
        (<button onClick={() => setButton(!button)}>
             CLICK ME
         </button>);

    return (
        {buttonElement}
        <OurGraph headerText={button === true ? 'true text' : 'false text'} />
    )
}

This is so close to working as intended... when the component initially renders, true text shows as the text in the SVG, good, And when we click on the text, the text is editable, good!

The issue is that when we click on the CLICK ME button, which changes the headerText prop in OurGraph from true text to false text , the text in the SVG in the OurGraph component does not update to false text , even though the prop value does successfully update to false text .

Why is this? How can we fix this so that the changing prop value is reflected in the text in the SVG's Form Control? We thought that the new headerText value would have led to const [graphText, setGraphText] = useState(headerText); setting a new value for graphText , but graphText is not changing when the headerText prop changes from true text to false text .

Edit - although it seems like an anti-pattern to immediately setState() from a props value, but we need graphText as a variable in state because it is updated in the FormControl, and we want the prop value headerText to be the default value for graphText . Most importantly, whenever headerText changes, we want to override whatever value may be in graphText set from the FormControl with the new prop value passed in headerText .

This is a classic example of an anti-pattern. React does not reinitialize the state. In order to make it work you can do following:

import { FormControl } from 'react-bootstrap';
   import {useEffect} from 'react';

   function OurGraph({ headerText }) {
    const [graphText, setGraphText] = useState(headerText);

    useEffect(()=>{
     setGraphText(headerText)
    },[headerText])

    // create editable header
    const ourHeader =
        (<foreignObject width='100%' height='40'>
            <div xmlns='http://www.w3.org/1999/xhtml'>
                <FormControl
                    className='modal-input no-style'
                    type='text'
                    value={graphText}
                    onChange={e => setGraphText(e.target.value)}
                    name='subheader'
                    placeholder=''
                />
            </div>
        </foreignObject>);

    // and return
    return (<svg>{ourHeader}</svg>);
 }

Moreover, a better approach would be taking this state into context and exporting the onChange function from context and use custom hook to manipulate it.

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