使用 componentDidUpdate 在反应中更新状态

Posted

技术标签:

【中文标题】使用 componentDidUpdate 在反应中更新状态【英文标题】:Updating state in react using componentDidUpdate 【发布时间】:2019-08-29 07:03:33 【问题描述】:

我无法在我的代码中更新状态

我的 componentDidUpdate() 出现问题,它在进行 API 调用后没有更新分配状态 更新作业时。当我更新列表中特定分配的到期日期时,它会对服务器进行 API 调用并返回 如果响应成功,则为 true。查看状态更新变化的唯一方法是刷新页面。组件DidUpdate() 陷入无限循环,任何人都可以找出造成这种情况的下划线原因吗?

感谢您的帮助

import * as React from 'react';
import './BundleAssignments.less';
import  IBundles, featureAccessApi, IAssignmentsByFirm, IBundleAssignment  from '@afi/tfs';
import  Loader  from '@afi/tfs';

export interface IOwnProps 

export interface IOwnState 
    loadingBundles: boolean,
    loadingAssignments?: boolean,
    loadingUpdate?: boolean,
    bundles: IBundles[],
    assignments: IAssignmentsByFirm[],
    expirationDate: string,
    bundleId?: number | undefined



export class BundleAssignments extends React.Component<IOwnProps, IOwnState> 
    constructor(props: IOwnProps) 
        super(props);
        this.state = 
            loadingBundles: true,
            bundles: [],
            assignments: [],
            expirationDate: "",
            bundleId: undefined
        ;
    

    public componentDidMount() 
        this.loadBundles();
    

    public componentDidUpdate(prevProps: IOwnProps, prevState: IOwnState)
    
        if (prevState.assignments !== this.state.assignments && this.state.bundleId !== undefined)

            this.loadBundleAssignments(this.state.bundleId);
        
    

    public render() 
        return (
            <div className="bundle-assignments">
                <h1>Bundle assignments</h1>
                
                    this.state.loadingBundles ? <Loader /> :
                    <>
                        <select onChange=e => this.onChangeSelectedBundle(e)>
                            <option value="">-- Select a Bundle --</option>
                            
                                this.state.bundles.map(b => 
                                    <option key=b.id value=b.id>b.name</option>
                                )
                            
                        </select>

                        
                            this.state.assignments != null && this.state.assignments.length > 0 ?
                                (this.state.loadingAssignments || this.state.loadingUpdate) ? <Loader /> :
                                <>
                                    <h1>Assignments</h1>
                                    <div className="download">
                                        <a href="https://localhost:44301/api/v2/admin/featureBundle/download/" + this.state.bundleId>Download Excel</a>
                                    </div>
                                    <table className="assignmentsTable">
                                        
                                            this.state.assignments.map(a => 
                                                <tr key=a.firmRef>
                                                    <th>
                                                        <span>a.firmName</span><br />
                                                        <a href="admin/teams/firm/" + a.firmRef>View teams</a>
                                                    </th>
                                                    <td>
                                                    
                                                        <ul id="entites">
                                                            
                                                                a.entities.map(e =>
                                                                    <li key=e.entityRef>
                                                                        <span>e.entityName</span>
                                                                    </li>
                                                                )
                                                            
                                                        </ul>
                                                    
                                                    </td>
                                                    <td>
                                                    
                                                        a.entities.map(e =>
                                                            <form key=e.entityRef onSubmit=(event) => this.handleSubmit(event, e.bundleAssignment.entityRef, e.bundleAssignment.bundleId, e.bundleAssignment.entityTypeId)>
                                                                <input type="datetime-local" name="expirationDate" defaultValue=e.bundleAssignment.expirationDate  onChange=this.handleInputChange />
                                                                <input type="submit" value="Update" />
                                                            </form>         
                                                        )
                                                    
                                                    </td>
                                                </tr>
                                            )
                                        
                                    </table>
                                </>
                            : null
                        
                    </>
                
            </div>
        )
    

    private loadBundles = () => 
        featureAccessApi.bundles()
            .then(response => this.loadBundlesSuccess(response.bundles));
    

    private loadBundlesSuccess = (bundles: IBundles[]) => 
        this.setState( ...this.state,
            ... 
                loadingBundles: false, 
                bundles: bundles
            
        ) 
    

    private onChangeSelectedBundle = (e: React.ChangeEvent<htmlSelectElement>) => 
        const bundleId = Number(e.target.value);
        this.setState( ...this.state, ... loadingAssignments: true, bundleId: bundleId  )
        this.loadBundleAssignments(bundleId);
    

    private handleSubmit = (e: React.FormEvent, entityRef: number, bundleId: number, entityTypeId: number) => 
        e.preventDefault();
        this.setState( ...this.state, ... loadingUpdate: true ) 
        this.updateBundleAssignment(entityRef, bundleId, entityTypeId);
    

    private handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => 
        const target = e.target;
        const value = target.value;
        const name = target.name;

        this.setState( ...this.state, 
            ... 
                [name]: value
             
        ) 
    

    private updateBundleAssignment = (entityRef: number, bundleId: number, entityTypeId: number) => 
        const request: IBundleAssignment = 
            entityRef: entityRef,
            bundleId: bundleId,
            entityTypeId: entityTypeId,
            expirationDate: this.state.expirationDate
        ;

        featureAccessApi.updateBundleAssignment(request)
            .then(response => this.bundleAssignmentUpdateSuccess());
    

    private bundleAssignmentUpdateSuccess = () =>
        this.setState( ...this.state, ... loadingUpdate: false ) 


    private loadBundleAssignments = (bundleId: number) => 
        featureAccessApi.bundleAssignments(bundleId)
            .then(response => this.loadBundleAssignmentsSuccess(response.assignmentsByFirms));
    

    private loadBundleAssignmentsSuccess = (bundleAssignments: IAssignmentsByFirm[]) => 
        this.setState( ...this.state,
            ... 
                loadingAssignments: false, 
                assignments: bundleAssignments
            
        ) 
    

【问题讨论】:

【参考方案1】:

将数组与!== 比较只会比较数组的引用而不是它们的内容,因此每次更新assignment 数组时,loadBundleAssignments 都会再次运行。

console.log([1,2] !== [1,2])

您可以改为使用例如Lodash isEqual 检查数组中的所有元素是否相互匹配。

public componentDidUpdate(prevProps: IOwnProps, prevState: IOwnState) 
  if (
    !_.isEqual(prevState.assignments, this.state.assignments) &&
    this.state.bundleId !== undefined
  ) 
    this.loadBundleAssignments(this.state.bundleId);
  

【讨论】:

以上是关于使用 componentDidUpdate 在反应中更新状态的主要内容,如果未能解决你的问题,请参考以下文章

反应钩子中的prevstate

如何检测组件将使用反应挂钩更新?

如何使用钩子清理 componentDidUpdate 中的异步函数

React app,componentDidUpdate - 跳转列表,无限循环

如何在反应中设置视频端组件的状态?

使用计算字段反应状态