Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update component state on redux state change?

Since I have a component with forms, I need the forms to be connected to the component state. The initial data comes from Redux so try to initialize and update the component by setting the state with the props:

componentWillMount = () => {
  this.setState({
    language: this.props.language || 'en'
  })
}

language is a connected prop and I checked that it is updated in the store.

const mapStateToProps = state => ({
  language: state.language
})

I also tried to use componentWillReceiveProps and componentWillUpdate but it doesn't work. I get the initial state, and even though the store and the connected props change, the component's state doesn't update.

{this.props.language} // updates
{this.state.language} // doesn't change

What is the correct way to manage forms from Redux data?


The render part:

render () {
  const {classes, theme, web} = this.props

  const language = (
    <CardContent>
      <Typography type="headline">
        Language
      </Typography>
      <Divider/>
      <form className={classes.container} autoComplete="off">
        <FormControl fullWidth margin="normal">
          <InputLabel htmlFor="language">Select Block</InputLabel>
          <Select
            value={this.state.language} // <==================== language
            onChange={this.handleLanguaheChange}
            input={<Input id="language"/>}
          >
            <MenuItem value={'en'}>English</MenuItem>
            <MenuItem value={'he'}>עברית</MenuItem>
          </Select>
        </FormControl>
      </form>
    </CardContent>
  )
  ...

  return (
      <Grid
        container
        spacing={theme.spacing.unit * 3}
        justify={'space-between'}
        className={classes.gridWrap}
      >
        <Grid item xs={6}>
          <Card className={classes.card}>
            {language}
          </Card>
...
like image 643
ilyo Avatar asked Dec 07 '17 12:12

ilyo


3 Answers

First, you are using an arrow function for componentWillMount. Rule of thumb is, do not use arrow functions for life-cycle hooks(componentWillMount, shouldComponentUpdate, ...etc). It's usual to setState in componentWillMount hook. But never set state in componentDidMount. please try to re-write it as,

constructor(props) {
 super(props)
 this.state = {
  language: 'en',
 }
}
componentWillMount() {
 const { language } = this.props
 if (language) this.setState(prevState => ({ language: prevState.language = language }))
}

in some exceptional cases, such as i wrote two classes in a single .js file(like i said, some exceptions) and i couldn't be able to modify it from componentWillMount as expected(later noted, the props are modified by the child class). in such cases, you can override it in render

render() {
 const { language } = this.props
 if (language) this.setState(prevState => ({ language: prevState.language = language }))
like image 137
pope_maverick Avatar answered Oct 17 '22 16:10

pope_maverick


To accomplish this with React Hooks:

  1. Track previous value with useRef()
  2. Compare with previous value and conditionally update the local component state

The posted example didn't really make sense to me so here is the problem I faced:

I have a form with component state that I needed to clear out when the redux state changed.

enter image description here

To accomplish this my component looks like this:

import { useSelector } from 'react-redux';
import React, { useState, useEffect, useRef } from 'react';
const CreateCase = () => {

  //redux state
  const accounts = useSelector(state => state.accounts);

  //component state
  const [productId, setProductId] = useState(undefined);

  const prevAccountRef = useRef<string>();
  useEffect(() => {
    //compare current with previous account and clear productId if changed
    if (account.id != prevAccountRef.current) {
      setProductId(undefined);
    }
    //set previous account for next render
    prevAccountRef.current = account.id;
  });

  //... render
}

It's very important that you only run setState inside of useEffect conditionally.

like image 45
NSjonas Avatar answered Oct 17 '22 16:10

NSjonas


even though the store and the connected props change, the component's state doesn't update

The way you have it written, the state won't update unless you explicitly update it using setState() (most likely in the componentWillReceiveProps() method).

When you use mapStateToProps() with the Redux connect() HOC, you are mapping your Redux state to your component through its props, so in your case this.props.language will update when the Redux stored updates.

like image 1
T Porter Avatar answered Oct 17 '22 16:10

T Porter