【问题标题】:React not re-rendering component after page refresh, even if the state changes页面刷新后反应不重新渲染组件,即使状态发生变化
【发布时间】:2019-08-31 13:04:58
【问题描述】:

我遇到了非常奇怪的问题。我的意思是,对我来说,真的。这不是某种“使用 setState 而不是 this.state”的问题。我正在开发旅行计划应用程序。我正在使用 ContextAPI,它为整个应用程序提供登录信息和用户数据(记录的用户行程等)。 “日程”组件,显示每天的行程日程是上下文订阅者。 现在我想删除其中一个行程日(Day,也订阅上下文是由 Schedule 组件在 Schedule 列表中呈现的组件)。然后神奇的事情发生了:

当我在登录应用程序后立即执行此操作时,一切正常。但是当我刷新页面并删除另一天时,上下文状态(正如我所说,它包含所有数据,包括日程表中的天数)更改(通过开发人员工具检查)但日程安排组件不会重新渲染本身,所以删除的元素仍然可见。 但正如我所说,状态发生变化,并且在删除操作之前没有页面刷新,一切正常。此外,当我切换到另一个选项卡时(计划只是其中一个选项卡,例如导航栏渲染成本和注释组件上的成本和注释选项卡),然后返回重新渲染的计划选项卡和所有内容看起来不错,组件根据上下文状态显示信息。

代码是这样工作的:删除(上下文函数)由 Day.js(上下文订阅者)中的删除图标点击触发,Day.js 由 Schedule.js(上下文订阅者也是)呈现。

在 Context.js 中

... context functions ...
    //WRAPPER FOR DELETE FUNCTIONS
    delete(url, deleteFunc) {
        this.requestsApi.request(url, "DELETE", null)
        .then(response => {
            if(response.status===204) {
                deleteFunc();
            } else {
                this.toggleRequestError(true);
            }
        })
        .catch( err => {
            console.log(err);
            this.toggleRequestError(true);
        });
    }

    deleteDay = (id, tripId) => {
        let newTrip = this.state.userData.trips.find((trip => trip.id === 
         tripId));
        newTrip.days = newTrip.days.filter(day => day.id !== id)

        this.updateTrip(newTrip);
    }

    updateTrip = (newTrip) => {
        let trips = this.state.userData.trips;

        this.setState(prevState => ({
            userData : {
                ...prevState.userData,
                trips: trips.map(trip => {
                    if(trip.id !== newTrip.id)
                        return trip;
                    else
                        return newTrip;
                    })
                }
            }
        ));
    }

...

在 Schedule.js 中

... in render's return ...
<ul>
    {this.trip.days.map((day,index) => {
         return <DayWithContext 
            inOverview={false} 
            key={index} 
            number={index} 
            day={day} 
            tripId={this.trip.id}    
         />
     })}
</ul>
....                 

在 Day.js 中(删除图标)

...
<img 
    src={Delete} 
    alt="Delete icon" 
    className="mr-2" 
    style={this.icon}
    onClick={() => {
             this.props.context.delete(
             `/days/${this.props.day.id}`, 
             () => this.props.context.deleteDay(this.props.day.id, 
                   this.props.tripId)
             );
    }}
/>
...

任何人都知道发生了什么以及为什么在页面刷新后删除完成后无法重新呈现计划?即使上下文状态发生变化?我现在唯一的想法是这是某种错误......

//更新 在 Schedule 组件中,在函数 componentWillReceiveProps() 我控制台日志 props.context.trip[0].day[0] - 第一天。它不是对象,只是纯文本,因此“当我在控制台中单击它时”不会对其进行评估。 在我删除它之前,控制台会记录第一项。在我删除它之后,控制台会记录第二个项目(所以现在它是列表中的第一个项目),所以提供给 Schedule 的道具正在改变,但没有触发渲染......那里到底发生了什么?

//更新 2 我还注意到,当我从另一个组件切换到 Schedule 组件时,它运行良好(例如,我正在刷新 /costs 端点上的页面,然后单击导航栏菜单上的 Schedule 选项卡,这会导致 /schedule)。但是当我在 /schedule 端点上刷新页面时,会发生此“错误”并且组件不会重新呈现。

//从上下文渲染:

...
    render() {        
        const {
            isLoggedIn,
            wasLoginChecked,
            userData,
            isLogoutSuccesfulActive,
            isLogoutUnSuccesfulActive,
            isDataErrorActive,
            isRequestErrorActive,
            isDataLoaded
        } = this.state;

        const context =  {
            isLoggedIn,
            wasLoginChecked,
            isLogoutSuccesfulActive,
            isLogoutUnSuccesfulActive,
            isDataErrorActive,
            isRequestErrorActive,
            userData,
            isDataLoaded,
            requestsApi : this.requestsApi,
            toggleLogin : this.toggleLogin,
            checkLogin: this.checkLogin,
            setUserData: this.setUserData,
            loadData: this.loadData,
            addTrip: this.addTrip,
            delete: this.delete,
            deleteDay: this.deleteDay,
            deleteActivity: this.deleteActivity,
            deleteTrip: this.deleteTrip,
            updateTrip: this.updateTrip,
            uncheckLogin: this.uncheckLogin,
            toggleLogoutSuccesful: this.toggleLogoutSuccesful,
            toggleLogoutUnSuccesful: this.toggleLogoutUnSuccesful,
            toggleRequestError: this.toggleRequestError,
            toggleDataError: this.toggleDataError
        };

        return (
            <Context.Provider value={context}>
                {this.props.children}
            </Context.Provider>
        )
    }
}

export const Consumer = Context.Consumer;

-------------上下文HOC:

export default function withContext(Component) {
    return function ContextComponent(props) {
        return (
            <Context.Consumer>
                {context => <Component {...props} context={context} />}
            </Context.Consumer>
    );
    }
}


【问题讨论】:

  • Schedule.js 中您使用的是this.trip。你是怎么设置的?

标签: javascript reactjs


【解决方案1】:

你改变了你的状态。 在这个地方,你改变一个对象的状态而不使用 setState:

newTrip.days = newTrip.days.filter(day => day.id !== id)

然后在你的 map 函数中,你总是返回相同的对象(它已经更新了)。 因此,要解决您的问题,您可以尝试更改您的 deleteDay 方法:

   deleteDay = (id, tripId) => {
        const trip = this.state.userData.trips.find((trip => trip.id === 
         tripId));
        const newTrip = {...trip, days: trip.days.filter(day => day.id !== id)};

        this.updateTrip(newTrip);
    }

更新: 您的Schedule 组件中还有一个问题。

需要动态获取当前行程,不要在构造函数中设置:

试试这个:

constructor(props) {
    super(props);
    this.getTrip = this.getTrip.bind(this);
}
getTrip() {
    return this.props.context.userData.trips.find(trip => {
        return `${trip.id}`===this.props.match.params.id;
    });
}
render() {
    const trip = this.getTrip();

    return (
        ...
        <ul>
            {trip.days.map((day,index) => {
                return <DayWithContext 
                    inOverview={false} 
                    key={day.id} 
                    number={index} 
                    day={day} 
                    tripId={trip.id}    
                />
            })}
        </ul>
        ...
    )
}

【讨论】:

  • 感谢您的回答 - 我忘记了 = 不是在复制数组。我使用以下方法解决了这个问题: const trips = Array.from(this.state.userData.trips);让 newTrip = trips.find((trip => trip.id === tripId));但它仍然没有解决我的问题。上下文状态得到更新,但调度组件保持不变。当我在登录后第一次看到该应用程序时 - 它工作正常。但是当我刷新页面(会话让我保持登录状态)并对列表中的任何其他元素执行相同的操作时,什么也没有发生。 Courtain 后面的状态发生变化,但没有触发渲染...
  • @hayaawama,您刚才显示的代码并不能解决数据突变的问题。如果您尝试在您的代码之后更改您的newTrip 变量,您还将更改您所在州的行程。试试这个代码:const arr = [{foo: "bar"}]; const newArr = Array.from(arr); newArr[0].foo = "baz"; console.log(arr[0].foo);(抱歉格式化,cmets 不允许使用美的格式化)你看,Array.from 不会创建新对象,它只是将链接复制到原始对象。您需要执行与我在回答中显示的完全相同的操作,您需要为您的 newTrip 创建一个新对象。
  • @hayaawama,也是我在您的 Schedule.js 中看到的潜在错误。您需要使用 day.id 作为组件的键:key={day.id}
  • 哦,所以我被一些网站误导了 Array.from。我已经完成了你所说的一切,还将 updateTrip 中的第一行更改为 const trips = [...this.state.userData.trips]。还是行不通。我也改变了钥匙(为什么,顺便说一句?)。如果您仍然愿意提供帮助,可以在主要问题底部查看我的更新说明 - 问题在其他地方,因为状态已更改,组件接收新道具,但未重新渲染:/
  • 谢谢哥们!!!!!!!!!!!!现在像魅力一样工作。尽管在构造函数中没有绑定,但使您的函数成为箭头。感谢您的宝贵时间,我从这次谈话中学到了很多。似乎我必须阅读很多关于组件生命周期和一般 React 的内容。但是现在我不明白为什么它甚至可以在第一时间工作(没有页面刷新),因为行程一直是固定的:D 还有一个问题,将函数放在 shouldComponentUpdate 中会产生相同的效果吗?
猜你喜欢
  • 1970-01-01
  • 2021-01-27
  • 1970-01-01
  • 2020-01-13
  • 2017-03-24
  • 1970-01-01
  • 2020-02-18
  • 1970-01-01
  • 2017-03-25
相关资源
最近更新 更多