React:当prop更改时,为什么子组件不会更新

时间:2016-08-11 09:40:36

标签: javascript html reactjs

为什么在以下伪代码示例中,当Container更改foo.bar时,Child不会重新呈现?

Container {
  handleEvent() {
    this.props.foo.bar = 123
  },

  render() {
    return <Child bar={this.props.foo.bar} />
}

Child {
  render() {
    return <div>{this.props.bar}</div>
  }
}

即使我在修改Container中的值后调用forceUpdate(),Child仍会显示旧值。

17 个答案:

答案 0 :(得分:51)

因为如果父级的道具发生变化,孩子不会重新渲染,但如果其状态发生变化,则不会重新渲染:)

你所展示的是: https://facebook.github.io/react/tips/communicate-between-components.html

它会通过道具将数据从父级传递给子级,但那里没有重新登记逻辑。

您需要将一些状态设置为父级,然后在父级更改状态下重新呈现子级。 这可能有所帮助。 https://facebook.github.io/react/tips/expose-component-functions.html

答案 1 :(得分:12)

更新子项以使属性“key”等于name:

Child {
  render() {
    return <div key={this.props.bar}>{this.props.bar}</div>
  }
}

答案 2 :(得分:10)

functionsuseState创建React组件时。

const [drawerState, setDrawerState] = useState(false);

const toggleDrawer = () => {
      // attempting to trigger re-render
      setDrawerState(!drawerState);
};

这确实 有效

         <Drawer
            drawerState={drawerState}
            toggleDrawer={toggleDrawer}
         />

起作用 (添加密钥)

         <Drawer
            drawerState={drawerState}
            key={drawerState}
            toggleDrawer={toggleDrawer}
         />

答案 3 :(得分:8)

根据React哲学组件无法改变其道具。它们应该从父母那里收到,并且应该是不可改变的。只有父母可以改变其子女的道具。

state vs props

的好解释

另外,请阅读此帖子Why can't I update props in react.js?

答案 4 :(得分:6)

您应该使用setState功能。如果没有,状态将不会保存您的更改,无论您如何使用forceUpdate。

Container {
    handleEvent= () => { // use arrow function
        //this.props.foo.bar = 123
        //You should use setState to set value like this:
        this.setState({foo: {bar: 123}});
    };

    render() {
        return <Child bar={this.state.foo.bar} />
    }
    Child {
        render() {
            return <div>{this.props.bar}</div>
        }
    }
}

您的代码似乎无效。我无法测试此代码。

答案 5 :(得分:3)

使用setState函数。 所以你可以做到

       this.setState({this.state.foo.bar:123}) 

在handle事件方法中。

一旦状态更新,它将触发更改,并将重新渲染。

答案 6 :(得分:2)

export default function DataTable({ col, row }) {
  const [datatable, setDatatable] = React.useState({});
  useEffect(() => {
    setDatatable({
      columns: col,
      rows: row,
    });
  /// do any thing else 
  }, [row]);

  return (
    <MDBDataTableV5
      hover
      entriesOptions={[5, 20, 25]}
      entries={5}
      pagesAmount={4}
      data={datatable}
    />
  );
}

此示例在useEffect更改时使用props更改状态。

答案 7 :(得分:1)

我有同样的问题。 这是我的解决方案,我不确定这是否是个好习惯,如果不能,请告诉我:

state = {
  value: this.props.value
};

componentDidUpdate(prevProps) {
  if(prevProps.value !== this.props.value) {
    this.setState({value: this.props.value});
  }
}

UPD:现在,您可以使用React Hooks做同样的事情: (仅当component是一个函数时)

const [value, setValue] = useState(propName);
// This will launch only if propName value has chaged.
useEffect(() => { setValue(propName) }, [propName]);

答案 8 :(得分:1)

确认,添加密钥即可。我浏览了文档以尝试并理解原因。

React希望在创建子组件时高效。如果它与另一个子组件相同,它将不会呈现新组件,这会使页面加载更快。

添加键会强制React渲染新组件,从而重置该新组件的状态。

https://reactjs.org/docs/reconciliation.html#recursing-on-children

答案 9 :(得分:0)

安装包 npm install react-native-uuid 要么 纱线添加 react-native-uuid

导入 从'react-native-uuid'导入uuid;

使用此方法为孩子的 uuid 将解决重新渲染问题 uuid.v4();

答案 10 :(得分:0)

您可以使用componentWillReceiveProps

componentWillReceiveProps({bar}) {
    this.setState({...this.state, bar})
}

归功于Josh Lunsford

答案 11 :(得分:0)

您一定使用过动态组件。

在此代码片段中,我们多次渲染子组件并传递key

  • 如果我们多次动态地渲染一个组件,那么 React 不会渲染该组件,直到它的键被更改。

如果我们使用 setState 方法更改 checked。在我们更改其之前,它不会反映在子组件中。我们必须将其保存在 child 的 state 中,然后相应地更改它以呈现 child。

class Parent extends Component {
    state = {
        checked: true
    }
    render() {
        return (
            <div className="parent">
                {
                    [1, 2, 3].map(
                        n => <div key={n}>
                            <Child isChecked={this.state.checked} />
                        </div>
                    )
                }
            </div>
        );
    }
}

答案 12 :(得分:0)

我的案例涉及在 props 对象上有多个属性,并且需要在更改其中任何一个时重新渲染 Child。 上面提供的解决方案是有效的,但是为每个解决方案添加一个密钥变得乏味和肮脏(想象一下有 15 个......)。如果有人遇到这种情况 - 您可能会发现将 props 对象字符串化很有用:

<Child
    key={JSON.stringify(props)}
/>

这样,props 上每个属性的每次更改都会触发 Child 组件的重新渲染。

希望对某人有所帮助。

答案 13 :(得分:0)

就我而言,我正在更新一个loading状态,该状态已传递给组件。在按钮内,props.loading正按预期进行(从false切换为true),但是显示微调框的三元组并没有更新。

我尝试添加密钥,添加使用useEffect()等更新的状态,但其他答案均无效。

对我有用的是改变这一点:

setLoading(true);
handleOtherCPUHeavyCode();

对此:

setLoading(true);
setTimeout(() => { handleOtherCPUHeavyCode() }, 1)

我认为是因为handleOtherCPUHeavyCode中的过程非常繁琐,因此应用程序冻结了大约一秒钟。添加1ms超时可以使加载布尔值更新,然后然后使用繁重的代码功能即可完成工作。

答案 14 :(得分:0)

在子组件的connect方法的mapStateToProps中定义更改的道具。

function mapStateToProps(state) {
  return {
    chanelList: state.messaging.chanelList,
  };
}

export default connect(mapStateToProps)(ChannelItem);

在我的情况下,channelList的频道已更新,因此我在mapStateToProps中添加了chanelList

答案 15 :(得分:0)

我遇到了同样的问题。 我有一个$template_html = ''; $template_txt = ''; $id_lang = $this->context->cart->id_lang; $iso = $this->context->language->iso_code; $iso_template = $iso.'/quotes_notify'; // $template_path = $theme_path.'modules/'.$module_name.'/mails/'; $template_path = PS_MODULE_DIR_ . $this->module->name . '/mails/'; Hook::exec('actionEmailAddBeforeContent', array( 'template' => quotes_notify, 'template_html' => &$template_html, 'template_txt' => &$template_txt, 'id_lang' => 1 ), null, true); $template_html .= Tools::file_get_contents($template_path.$iso_template.'.html'); print_r($template_html); die(); 组件正在接收Tooltip道具,我正在根据showTooltip条件在Parent组件上进行更新,该组件正在if中进行更新组件,但Parent组件未呈现。

Tooltip

我做的错误是将const Parent = () => { let showTooltip = false; if(....){ showTooltip = true; } return( <Tooltip showTooltip={showTooltip}></Tooltip> ) } 声明为let。

我意识到我做错了我违反了渲染工作原理的工作,用钩子代替了工作。

showTooltip

答案 16 :(得分:0)

如果不保持任何状态而只是渲染道具然后从父级调用它,则应该使Child作为功能组件。替代方法是,可以将钩子与功能组件(useState)一起使用,这将导致无状态组件重新呈现。

此外,您不应更改道具,因为它们是不可变的。维护组件的状态。

Child = ({bar}) => (bar);