【问题标题】:useState hook not updating a valueuseState 挂钩不更新值
【发布时间】:2019-12-10 15:16:53
【问题描述】:

我是新手,我不明白为什么 h1 标签内的 title 会更新,但 Image 组件内的 url 却没有?

上市组件

import React, { useState, useEffect, useContext } from 'react';
import Image from './Image';
import Size from '../index';

export default function Listing(props) {
  const [title, setTitle] = useState(props.title);
  const [url, setUrl] = useState(props.url);

  const value = useContext(Size);

  return (
    <div>
      <Image url={url} size={value.size} />

      <h1>{title}</h1>
      <form>
        <input id='URL' type='text' />
        <button
          onClick={e => {
            e.preventDefault();
            setUrl(document.getElementById('URL').value);
            setTitle(document.getElementById('URL').value);
          }}
        >
          Submit
        </button>
      </form>
    </div>
  );
}

到目前为止,我的猜测是,如果 prop 更改,React 不会更新子组件,但是我该如何手动更新呢?

图像组件

import React from 'react';

export default class Image extends React.Component {
  //console.log(props.size);
  constructor(props) {
    super(props);
    this.url = props.url;
    this.size = props.size;
  }
  render() {
    return <img src={this.url} style={{ height: this.size + 'px' }} alt='' />;
  }
}```

【问题讨论】:

标签: javascript reactjs jsx


【解决方案1】:

您的代码有很多“气味”。这是简短的快速修复答案:

将这个:src={this.url} 改为这个:src={this.props.url}

图片从不更新的原因是这样的:

constructor(props) {
  super(props);
  this.url = props.url;
  this.size = props.size;
}

您正在将局部变量设置为初始道具值。由于您是在 constructor 中设置这些,因此这些行只会在创建组件时执行,而不会在发送新道具时执行。React 仍在触发重新渲染,因为您是发送新的 props,但从未使用过新的值,因此保留旧的结果。


答案稍长:

像您在此处所做的那样直接混合从 DOM 读取的值并不是一个好主意:

setUrl(document.getElementById('URL').value);
setTitle(document.getElementById('URL').value);

相反,有 2 个状态。一个保存您输入的当前值,每次击键都会更新,另一个保存您发送到 Image 组件的值。

可能是这样的:

export default function Listing(props) {
  const [title, setTitle] = useState(props.title);
  const [inputUrl, setInputUrl] = useState(props.url);
  const [url, setUrl] = useState(props.url);

  const value = useContext(Size);

  return (
    <div>
      <Image url={url} size={value.size} />

      <h1>{title}</h1>
      <form>
        <input
          value={inputUrl}
          onChange={e => setInputUrl(e.target.value)}
        />
        <button
          type="button"
          onClick={() => {
            setUrl(inputUrl);
            setTitle(inputUrl);
          }}
        >
          Submit
        </button>
      </form>
    </div>
  );
}

还请注意,我删除了e.preventDefault() 并将type="button" 添加到您的按钮,因为默认类型是submit,这可能会刷新您的页面。

【讨论】:

  • 这终于是这里最完整(最正确)的答案了。
【解决方案2】:

您不需要将 props 值分配给子组件中的类变量。由于您在构造函数中执行此操作,因此不会更新。

更改您的 Image 组件以直接使用来自 props 的数据

import React from 'react';

export default class Image extends React.Component {
  //console.log(props.size);
  constructor(props) {
    super(props);
  }
  render() {
    return <img src={this.props.url} style={{ height: this.props.size + 'px' }} alt='' />;
  }
}

【讨论】:

  • 只是在render 函数中缺少this.props,因为这是一个类组件。
  • @EmileBergeron 感谢您的编辑,我遇到了一些事情,没有验证我的答案
【解决方案3】:

您的图像组件没有正确处理道具。将 prop 值分配给类变量不会像您期望的那样工作,因为此代码只执行一次(因为它在构造函数内部)。

  constructor(props) {
    super(props);
    // The following causes trouble.
    this.url = props.url;
    this.size = props.size;
  }

您可以在构造函数中分配一个状态,如下所示,但这并不能解决您的问题。您需要在某个地方相应地更新状态。有多种生命周期方法可供选择,匹配的一种是:UNSAFE_componentWillReceiveProps,但顾名思义,我不会再使用它了。

constructor(props) {
  super(props);
  this.state = {
    url = props.url,
    size = props.size,
  };
}

基本上,您可以通过将道具传递到 JSX 并摆脱构造函数来简单地解决您的问题,因为您不需要根据输入值进行任何状态计算,因为这些值是 url 和大小。

import React from 'react';

export default class Image extends React.Component {
  render() {
    return <img src={this.props.url} style={{ height: this.props.size + 'px' }} alt='' />;
  }
}

【讨论】:

  • 设置一个状态并且以后不更新它与设置实例变量是一样的。
  • @EmileBergeron 谢谢,你是对的。相应地进行了编辑。
  • 您现在建议使用已弃用的生命周期挂钩。现在有getDerivedStateFromPropscomponentDidUpdate
猜你喜欢
  • 2021-06-06
  • 2019-05-12
  • 2022-01-21
  • 2021-04-13
  • 2020-05-31
  • 2021-03-30
  • 2021-11-25
  • 2021-09-22
  • 2021-12-19
相关资源
最近更新 更多