简体   繁体   English

如何在 redux 状态更改时更新组件状态?

[英]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:初始数据来自 Redux,因此尝试通过使用 props 设置状态来初始化和更新组件:

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

language is a connected prop and I checked that it is updated in the store. language是一个连接道具,我检查了它是否在商店中更新。

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

I also tried to use componentWillReceiveProps and componentWillUpdate but it doesn't work.我也尝试使用componentWillReceivePropscomponentWillUpdate但它不起作用。 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?从 Redux 数据管理表单的正确方法是什么?


The render part: render部分:

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>
...

First, you are using an arrow function for componentWillMount.首先,您正在为 componentWillMount 使用箭头函数。 Rule of thumb is, do not use arrow functions for life-cycle hooks(componentWillMount, shouldComponentUpdate, ...etc).经验法则是,不要将箭头函数用于生命周期钩子(componentWillMount、shouldComponentUpdate 等)。 It's usual to setState in componentWillMount hook.通常在 componentWillMount 钩子中设置状态。 But never set state in componentDidMount.但永远不要在 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).在某些特殊情况下,例如我在一个 .js 文件中编写了两个类(就像我说的,有些例外)并且我无法按预期从 componentWillMount 修改它(后来指出,道具是由孩子修改的类)。 in such cases, you can override it in render在这种情况下,您可以在渲染中覆盖它

render() {
 const { language } = this.props
 if (language) this.setState(prevState => ({ language: prevState.language = language }))

To accomplish this with React Hooks:要使用 React Hooks 完成此操作:

  1. Track previous value with useRef()使用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.我有一个带有组件状态的表单,当 redux 状态改变时我需要清除它。

在此处输入图片说明

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.仅在useEffect有条件地运行setState非常重要。

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).按照您编写的方式,状态不会更新,除非您使用setState() (最有可能在componentWillReceiveProps()方法中)显式更新它。

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.当您将mapStateToProps()与 Redux connect() HOC 一起使用时,您正在通过其props将您的 Redux 状态映射到您的组件,因此在您的情况下, this.props.language将在 Redux 存储更新时更新。

componentWillReceiveProps will be called only when your component re-rendered. componentWillReceiveProps只会在你的组件重新渲染时被调用。

Initially when component first time mounting, it not going to triggered.最初当组件第一次安装时,它不会触发。

You can not call setState inside componentwillupdate .您不能在componentwillupdate内调用setState

In order to initialise the initial state of component from redux store,you should use constructor .为了从 redux store 初始化组件的初始状态,你应该使用constructor

    constructor(props) {

    super(props);

    this.state = {
        language: this.props.language || 'en'
     }
   }

Not sure if this applies, but I ran into a similar problem recently and my case was due to the fact that calling this.setState does not guarantee an instant update to state;不确定这是否适用,但我最近遇到了类似的问题,我的情况是由于调用 this.setState 并不能保证立即更新状态; it only says that the state change will be effected eventually.它只是说最终会影响状态更改。

From the react-component documentation:从反应组件文档:

Think of setState() as a request rather than an immediate command to update the component.将 setState() 视为更新组件的请求而不是立即命令。 For better perceived performance, React may delay it, and then update several components in a single pass.为了获得更好的感知性能,React 可能会延迟它,然后一次更新多个组件。 React does not guarantee that the state changes are applied immediately. React 不保证状态更改会立即应用。

setState() does not always immediately update the component. setState() 并不总是立即更新组件。 It may batch or defer the update until later.它可能会批量更新或推迟更新。 This makes reading this.state right after calling setState() a potential pitfall.这使得在调用 setState() 之后立即读取 this.state 成为一个潜在的陷阱。 Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied.相反,请使用 componentDidUpdate 或 setState 回调 (setState(updater, callback)),这两者都保证在应用更新后触发。 If you need to set the state based on the previous state, read about the updater argument below.如果您需要根据之前的状态设置状态,请阅读下面的更新程序参数。

If you need to make some "instant" changes to state or things that depend on state, then there's that callback on setState() that you can use to lock things in place.如果您需要对状态或依赖状态的事物进行一些“即时”更改,则可以使用 setState() 上的回调将事物锁定到位。 The callback is what worked for me.回调对我有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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