呈现其他组件时无法更新组件("App") [英] Cannot update a component (`App`) while rendering a different component

查看:494
本文介绍了呈现其他组件时无法更新组件("App")的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有很多类似的问题,但是我找不到与我的难题相符的问题.

There are a bunch of similar questions on so, but I can't see one that matches my conundrum.

我有一个反应组件(一个径向旋钮控件-有点像一个滑块).我想实现两个结果:

I have a react component (a radial knob control - kinda like a slider). I want to achieve two outcomes:

  1. 旋转旋钮,并将旋钮值传递给父级以进行进一步的操作.
  2. 从父级接收目标旋钮值,并相应地更新旋钮.
  3. 一切都不会陷入无限循环!

我已经拔掉头发-但是有一个可行的解决方案似乎违反了反应原则.

I have pulled my hair out - but have a working solution that seems to violate react principles.

我有 knob.js 作为包裹第三方旋钮组件的react组件,而我有 app.js 作为父组件.

I have knob.js as a react component that wraps around the third party knob component and I have app.js as the parent.

在结子.js中,我们有:

In knob.js, we have:

export default class MyKnob extends React.Component {
    constructor(props, context) {
        super(props, context)

        this.state = {
            size: props.size || 100,
            radius: (props.value/2).toString(),
            fontSize: (props.size * .2)
        }
        if (props.value){
            console.log("setting value prop", props.value)
            this.state.value = props.value
        } else {
            this.state.value = 25           // any old default value
        }

      }

要处理来自父代(app.js)的更新,我将其放在结子.js中:

To handle updates from the parent (app.js) I have this in knob.js:

      // this is required to allow changes at the parent to update the knob
      componentDidUpdate(prevProps) {
        if (prevProps.value !== this.props.value) {
           this.setState({value: this.props.value})
        }
        console.log("updating knob from parent", value)
      }

然后将旋钮值的更改传递回父级,我有:

and then to pass changes in knob value back to the parent, I have:

    handleOnChange = (e)=>{
        //this.setState({value: e})    <--used to be required until line below inserted. 
        this.props.handleChangePan(e)
      }

这也有效,但会触发警告:

This also works but triggers a warning:

在渲染其他组件(旋钮)时无法更新组件( App )

render(){
        return (
            <Styles font-size={this.state.fontSize}>
            <Knob size={this.state.size}  
                angleOffset={220} 
                angleRange={280}
                steps={10}
                min={0}
                max={100}
                value={this.state.value}
                ref={this.ref}
                onChange={value => this.handleOnChange(value)}
            >
...

现在转到app.js:

Now over to app.js:

function App() {
  const [panLevel, setPanLevel] = useState(50);

// called by the child knob component. works -- but creates the warning
    function handleChangePan(e){
      setPanLevel(e)
    }

    // helper function for testing
    function changePan(e){
      if (panLevel + 10>100){
        setPanLevel(0)
      } else {
        setPanLevel(panLevel+10)
      }
    }

return (
    <div className="App">
        ....
        <div className='mixer'>
          <div key={1} className='vStrip'>
            <Knob size={150} value={panLevel} handleChangePan = {(e) => handleChangePan(e)}/>
          </div>
        <button onClick={(e) => changePan(e)}>CLICK ME TO INCREMENT BY 10</button>
      ...
    </div>

所以-它起作用了-但是我违反了反应原理-我还没有找到使外部旋钮值"和内部旋钮值"保持同步的另一种方法.

So - it works -- but I am violating react principles -- I haven't found another way to keep the external "knob value" and the internal "knob value" in sync.

只要进一步弄乱我的头,如果我在"handleOnChange"中消除了对父级的冒泡-大概会触发prop的更改->状态向下层叠-我不仅不与父级缺乏同步-但我还需要恢复下面的setState,以便通过旋转(鼠标等._)使旋钮起作用!这会产生另一个警告:

Just to mess with my head further, if I remove the bubbling to parent in 'handleOnChange' - which presumably then triggers a change in prop-->state cascading back down - I not only have a lack of sync with the parent -- but I also need to reinstate the setState below, in order to get the knob to work via twiddling (mouse etc.._)! This creates another warning:

在现有状态转换过程中更新...

Update during an existing state transition...

太难受了.要求提供建议并深表感谢.长篇文章的Apols.

So stuck. Advice requested and gratefully received. Apols for the long post.

    handleOnChange = (e)=>{
        //this.setState({value: e})
        **this.props.handleChangePan(e)**
      }

在另一篇文章中建议,应该将setState包装到useEffect中-但是我不知道该怎么做-更不用说这是否是正确的方法了.

It has been suggested on another post, that one should wrap the setState into a useEffect - but I can't figure out how to do that - let alone whether it's the right approach.

推荐答案

如果在渲染子级(旋钮)时设置了父级(App)状态,则会显示错误消息.

The error message will be displayed if parent (App) states are set while rendering children (Knob).

在您的情况下,正在渲染App时,旋钮的 onChange()会在加载时触发,然后调用 this.handleOnChange() this.具有App的 setPanLevel()的props.handleChangePan().

In your case, while App is rendering, Knob'sonChange() is triggered when loaded, which then calls this.handleOnChange() and then this.props.handleChangePan() having App'ssetPanLevel().

要使用 useEffect()进行修复:

  1. knob.js 中,您可以像在App中一样先将 panLevel 作为状态存储,而不是直接调用 this.props.handleChangePan()来调用App的 setPanLevel().
  2. 然后,使用 useEffect(_ => props.handleChangePan(panLevel),[panLevel])通过 useEffect()调用应用的 setPanLevel().
  1. In knob.js, you can store panLevel as state first just like in App, instead of direct calling this.props.handleChangePan() to call App'ssetPanLevel().
  2. Then, use useEffect(_=>props.handleChangePan(panLevel),[panLevel]) to call App'ssetPanLevel() via useEffect().

您的toggle.js如下所示:

Your knob.js will look like this:

function Knob(props){
  let [panLevel, setPanLevel] = useState(50);
  useEffect(_=>{
    props.handleChangePan(panLevel);
  }, [panLevel]);
  return *** Knob that does not call props.handleChangePan(), but call setPanLevel() instead ***;
}

在渲染完成后,在 useEffect()内部调用的

setState()将会生效.

setState() called inside useEffect() will be effective after the render is done.

简而言之,在第一次渲染时,您不能在 useEffect()外部调用父级的 setState(),否则会出现错误消息.

In short, you cannot call parent'ssetState() outside useEffect() while in first rendering, or the error message will come up.

这篇关于呈现其他组件时无法更新组件("App")的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆