useState钩子不更新值

时间:2019-12-10 15:16:53

标签: javascript reactjs jsx

我是新来的反应者,我不明白为什么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>
  );
}

到目前为止,我的猜测是如果道具发生变化,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='' />;
  }
}```

3 个答案:

答案 0 :(得分:4)

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

更改您的图片组件以直接使用道具中的数据

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='' />;
  }
}

答案 1 :(得分:3)

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

将此:src={this.url}更改为:src={this.props.url}

图像从未更新的原因是因为:

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

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


答案略长:

像在这里一样直接混合从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 :(得分:0)

您的图像组件无法正确处理道具。将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='' />;
  }
}