Ad

React: SetState Does Not Update Visual Component

A function component does not perform its visual update when the state changes.

I provide a minimal example of my component where I set a new state but the component does not update in the browser.

import * as React from "react"
import { Button, makeStyles, Grid, TextField, createStyles } from '@material-ui/core'
import ReactDOM from "react-dom";


const styles = makeStyles(theme => createStyles({
  container: {
    display: 'flex',
    flexWrap: 'wrap',
  },
  textField: {
    marginLeft: theme.spacing(1),
    marginRight: theme.spacing(1),
  },
  button: {
    margin: theme.spacing(1),
  }  
}))


export interface RTestData {
    title: string
}

interface Props {
  data: RTestData[]
}

const testData: RTestData[] = [{title: "AA"}, {title: "BB"}, {title: "CC"}, {title: "DD"}, {title: "EE"}]

export const RTestContainer: React.FunctionComponent<Props> = (props) => {
    const [count, setCount] = React.useState(0)
    const [data, setData] = React.useState(props.data[count])

    const classes = styles()

    const handleClick = () => (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
        console.log(`BUTTON click`)
        const target = e.target
        setCount(count + 1)
        setData(props.data[count])
        console.log("data " + JSON.stringify(props.data) + "  ->" + JSON.stringify(data))
    }

    return (
        <Grid container spacing={2}>
            <Grid item xs={12}>
                <RTestForm title={data.title}/>
            </Grid>

            <Grid item>
                    <Button
                    variant="contained"
                    color="primary"
                    className={classes.button}
                    onClick={handleClick()}
                    >
                    Next!!
                </Button>
            </Grid> 
        </Grid>
    )
}

export const RTestForm: React.FunctionComponent<RTestData> = (props) => {
    const [title, setTitle] = React.useState(props.title)

    const handleChange = () => (e: React.ChangeEvent<HTMLInputElement>) => {
        const target = e.target
        const value = target.value
        console.log("Title change ->" + title)
        setTitle(value)
    }

    const classes = styles()

    return (
        <form className={classes.container} noValidate autoComplete="off">
            <Grid container spacing={2}>
                <Grid item xs={12} sm={6} lg={6}>
                    <TextField
                        required
                        label="Title"
                        value={title}
                        onChange={handleChange()}
                        className={classes.textField}
                        margin="normal"
                        variant="filled"
                    />
                </Grid>
            </Grid>
        </form>
    )
}


ReactDOM.render(<div id="root">
        <RTestContainer data={testData}/>
    </div>, document.getElementById('root'));

A click on the button should select and display the next data item, e. g. the textfield displays "AA", "BB" and so on.

Ad

Answer

On handleClick you change RTestContainer state but it doesn't affect RTestForm state.

//                 v On handleClick changes `data` state
<RTestForm title={data.title} />

//  Changing props won't re-initial the state. v
const [title, setTitle] = React.useState(props.title);

Try adding a listener for props.title change with useEffect:

export const RTestForm: React.FunctionComponent<RTestData> = props => {
  const [title, setTitle] = React.useState(props.title);

  React.useEffect(() => {
    setTitle(props.title);
  }, [props.title]);

  return <>...</>

};

Edit Q-58579793-useEffectListener

Ad
source: stackoverflow.com
Ad