React Hooks 渲染的钩子比上一次渲染时更多

Posted

技术标签:

【中文标题】React Hooks 渲染的钩子比上一次渲染时更多【英文标题】:React Hooks Rendered more hooks than during the previous render 【发布时间】:2021-05-31 04:22:17 【问题描述】:

一个呈现已填写表单预览的组件,当您单击左侧的表单时,它会在右侧显示它们。

第一个 handleSwitchReview 向我抛出 React Hooks Rendered more hooks than during the previous render error

第二个没有。例如,当我控制台记录道具时,当显示第一个功能的视图时,我得到了 4-5 次,但不是第二个,第二个仅在控制台日志中显示 1 次。

尝试移动 setState 并在控制台中记录父级,但这个组件是唯一一个多次触发并中断的组件,也许我只是对如何构建它没有深入的了解。

const SimpleFormPreview = (props) => 
    //Deconstructing Props
    const  childAndButtonWithStylesForPreview  = props;

    //Setting State
    const [child, setChild] = useState(
        childToDisplay: childAndButtonWithStylesForPreview ? childAndButtonWithStylesForPreview[0].child : 
    );

    //Deconstructing State
    const  childToDisplay  = child;

    const handleSwitchReview = (childWithIconArr) => 
        setChild( childToDisplay: childWithIconArr.child );
    ;

    const renderPreview = () => 
        if (childToDisplay.hasOwnProperty('schema')) 
            return <SimpleFormView children=undefined schema=childToDisplay.schema onValChange= onSatisfiedOrPercentageChange= vals= existingLookupOriginalVals= nonStandardKeysInPropInfo= satisfiedOverrides= />;
         else 
            var reviewItems = _.map(childToDisplay, function (val, key) 
                console.log('here', val, key);
                if (!key.startsWith('customer') && (key.startsWith('custom') || key.endsWith('Cost'))) 
                    //skip rendering these
                    //they will be attached to the main settings rendering below
                    return null;
                 else 
                    if (key === '_id' || key === 'id') 
                        return null;
                    

                    var costEl;
                    var customEl;

                    _.each(childToDisplay, function (customOrCostVal, customOrCostKey) 
                        if (customOrCostKey === 'custom' + key) 
                            customEl = (
                                <div
                                    style=
                                        display: 'inline-block',
                                        marginLeft: 5,
                                        color: 'rgb(222, 222, 0)'
                                    >
                                    'Customized to ' + customOrCostVal.toString()
                                </div>
                            );
                        

                        if (customOrCostKey === key + 'Cost') 
                            costEl = (
                                <div
                                    style=
                                        display: 'inline-block',
                                        color: 'rgba(39, 204, 39, 0.52)',
                                        marginRight: 20
                                    >
                                    '+ $' + customOrCostVal.val
                                </div>
                            );

                            totalAdditionalCost = totalAdditionalCost + customOrCostVal.val;
                        
                    );
                    return (
                        <div key=key>
                            costEl
                            <div style= display: 'inline-block', marginLeft: 5 >key.toString()</div>:<div style= display: 'inline-block', marginLeft: 5 >val.toString()</div>
                            customEl
                        </div>
                    );
                
            );
            return reviewItems;
        
    ;

    return (
        <Grid container direction='column' justify='flex-start' spacing=0>
            <Grid item xs=12>
                <Grid container wrap='wrap' spacing=0>
                    <Grid style= position: 'fixed', width: 100  container direction='column'>
                        <div style= marginLeft: 'auto', height: 447, overflowY: 'scroll', direction: 'rtl', background: 'transparent' >
                            _.map(childAndButtonWithStylesForPreview, function (childObj, idx) 
                                var innerIconElText = '';
                                // if ((idx + 1) > 99) 
                                //     innerIconElText = '...' + (idx + 1)
                                //  else 
                                //     innerIconElText = idx + 1
                                // 
                                if (childObj.iconEl) 
                                    childObj.iconEl.props.style.boxShadow = childToDisplay.id === childObj.child.id ? 'green 0px 0px 33px 5px' : null;
                                    return <div onClick=() => handleSwitchReview(childObj) key=idx>childObj.iconEl</div>;
                                 else 
                                    return (
                                        <Button style= boxShadow: childToDisplay.id === childObj.child.id ? 'green 0px 0px 33px 5px' : null, height: 100, margin: '25px', borderRadius: 60  key=idx onClick=() => handleSwitchReview(childObj)>
                                            <span id='defaultChildIcon' style= position: 'relative' ></span>
                                            <span style= marginLeft: 22, marginTop: 10, fontSize: 12, position: 'absolute' >innerIconElText</span>
                                        </Button>
                                    );
                                
                            )
                        </div>
                    </Grid>
                    <Grid id='simpleForm' style= height: '100%', width: '100%', overflow: 'auto'  container>
                        <Paper style= marginTop: '25px', marginLeft: '165px', padding: '15px', width: '80%'  elevation=24>
                            hello
                            <div style= float: 'right' >
                                <Button id='localSaveBtn' onClick=() => handleSave(document.querySelector('#simpleForm')) variant='contained' color='secondary' size='large' style=>
                                    Save File
                                </Button>
                            </div>
                        </Paper>
                    </Grid>
                </Grid>
            </Grid>
        </Grid>
    );
;

export default SimpleFormPreview;

【问题讨论】:

您能否在您的代码中添加更多内容 sn-p -> 如何渲染 SimpleFormPreview 绝对不是一个大文件,所以我会插入整个文件。好的.... 我无法立即在该文件中看到任何看起来会引发该错误的内容。您是否使用像&lt;SimpleFormPreview ... /&gt; 这样的创建元素来渲染它,因为我隐约记得一旦调用像SimpleFormPreview() 这样的组件,它就会为我抛出那个错误。您是否有任何示例说明您为 childAndButtonWithStylesForPreview 之类的道具传递了哪些数据? 道具示例= [ “id”:0,“child”:“_id”:“60143a2576ee585c52ffdfe0”,“partNumber”:“a”,“createdBy”:“Jacob1 Wazydrag”,“ createdOn": "2021-01-29T16:39:01.012Z", "id": "1611938341138" ] 我如何在父级中呈现它:返回 【参考方案1】:

在 if 语句“if (childObj.iconEl)”之后直接改变 prop 会导致应用重复重新渲染。

使用 cloneElement 我能够让我的元素将原始元素的道具与新道具浅层合并:reactjs.org/docs/react-api.html#cloneelement 错误停止并且我能够实现所需结果是更改作为道具传递给子元素的按钮的单击框阴影。

下面是替换“ if (childObj.iconEl)” If Condition 后面的行的代码。

                                    var clonedElementWithInjectedStyles = React.cloneElement(childObj.iconEl,  style:  ...childObj.iconEl.props.style, boxShadow: childToDisplay.id === childObj.child.id ? 'green 0px 0px 33px 5px' : null  );
                                    return (
                                        <div onClick=() => handleSwitchReview(childObj) key=idx>
                                            clonedElementWithInjectedStyles
                                        </div>
                                    );

【讨论】:

以上是关于React Hooks 渲染的钩子比上一次渲染时更多的主要内容,如果未能解决你的问题,请参考以下文章

React - 出现“渲染的钩子比上一次渲染时更多”错误

React/NextJS 使用 UseEffect 错误:渲染的钩子比上一次渲染期间更多

未处理的拒绝(错误):渲染的钩子比上一次渲染时更多(React Interface GrandStack with ApolloClient)

使用 useEffect 渲染的钩子比上一次渲染时更多

当钩子的初始值是来自数据库的查询结果时,“比上一次渲染期间渲染的钩子更多”错误

错误:渲染的钩子比上一次渲染时更多