渲染不同组件时无法更新组件(`App`)

Cannot update a component (`App`) while rendering a different component(渲染不同组件时无法更新组件(`App`))
本文介绍了渲染不同组件时无法更新组件(`App`)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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 作为一个反应组件,它包裹着第三方旋钮组件,我有 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.

在knob.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) 的更新,我在 kub.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:

在渲染不同的组件(Knob)时无法更新组件(App)

Cannot update a component (App) while rendering a different component (Knob)

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"中删除对父级的冒泡——这可能会触发道具的变化——>状态级联回落——我不仅与父级缺乏同步- 但我还需要恢复下面的 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...

所以卡住了.请求和感激地收到建议.长篇文章的 Apol.

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.

推荐答案

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

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

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

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()code> 调用 App 的setPanLevel().
  2. 然后,使用useEffect(_=>props.handleChangePan(panLevel),[panLevel])通过useEffect()调用App的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().

您的knob.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()之外调用parent的setState(),否则会报错.

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

这篇关于渲染不同组件时无法更新组件(`App`)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

Update another component when Formik form changes(当Formik表单更改时更新另一个组件)
Formik validation isSubmitting / isValidating not getting set to true(Formik验证正在提交/isValiating未设置为True)
React Validation Max Range Using Formik(使用Formik的Reaction验证最大范围)
Validation using Yup to check string or number length(使用YUP检查字符串或数字长度的验证)
Updating initialValues prop on Formik Form does not update input value(更新Formik表单上的初始值属性不会更新输入值)
password validation with yup and formik(使用YUP和Formick进行密码验证)