2

我有一个奇怪的问题,在我更新课程后,React 似乎正在缓存我的 API GET 请求。最近刚开始学习 React,所以有很多新东西要学 :)

过程:

首先,我转到 courseList.js,其中列出了所有课程。然后,我转到 courseUpdate.js,它会更新特定课程并重定向回 courseList.js

但是,在我更新课程后,我被重定向回 courseList.js 并输出旧数据(在我更新之前)。我检查了我的 API 服务器,发现我的 React 应用在 PATCH(更新)之后没有发送任何请求。在我的控制台中,我还看到数据是陈旧的,但时间戳是最新的。此问题仅在我使用时发生

this.props.router.push('/courses');
or
browserHistory.push({pathname: '/courses'});

当我使用

window.location.href = '/courses';

courseList.js 按预期加载新数据。

任何帮助或见解将不胜感激。

谢谢!

courseList.js 文件:

constructor(props) {
    super(props);

    this.state = {
        courses: [],
        loading: true
    };

    console.log("Current token: "+sessionStorage.getItem('access_token'));
    console.log("Expires: "+sessionStorage.getItem('access_token_expires'));
}

componentDidMount() {
    fetchCourses()
        .then((data) => {
            this.setState(state => {
                console.log(new Date().toLocaleString());
                console.log(data);
                if(data["error"] === "invalid_grant"){
                    console.log("token expired...redirecting to login");
                    //TODO try to get new token without user redirect
                    this.props.router.push('/login');
                }else{
                    state.courses = data;
                    state.loading = false;
                    return state;
                }

            })
        })
        .catch((err) => {
            console.error('err', err);
        });
}

render() {
    let loading = this.state.loading ? 'loading' : 'loaded';
    return (
        <div>
            <h1>Course list</h1>

            <table className={"table table-hover table-responsive text-center " +loading}>
                <thead>
                <tr>
                    <th className="text-center">id</th>
                    <th className="text-center">Department</th>
                    <th className="text-center">Course Number</th>
                    <th className="text-center">Edit</th>
                </tr>
                </thead>
                <tbody>
                {this.state.courses && this.state.courses.map((post, i ) => {
                    return (
                        <tr key={post.id}>
                            <td>{post.id}</td>
                            <td>{post.department}</td>
                            <td>{post.course_number}</td>
                            <td>
                                <Link to={`/courses/${post.id}`} className="btn btn-default btn-sm">Edit</Link>
                                <btn onClick={this.deleteHandler.bind(this, post.id)} className="btn btn-danger btn-sm">Delete</btn>
                            </td>
                            <td>

                            </td>
                        </tr>
                    );
                })}

                </tbody>
            </table>
            <Link to={`/courses/create`} className="btn btn-default btn-sm">Create course</Link>
            <br/>
            <small>Page generated on: {new Date().toLocaleString()}</small>
        </div>
    );
}

courseUpdate.js

getInitialState() {
    return {
      courses: {}
    };
},

componentDidMount() {
    fetchCourse(this.props.params.courseId)
      .then((data) => {
        this.setState(state => {
          console.log(data)
          if(data["error"] === "invalid_grant"){
            console.log("token expired...redirecting to login");
            //TODO try to get new token without user redirect
            this.props.router.push('/login');
          }else{
            state.courses = data;
            return state;
          }

        })
      })
      .catch((err) => {
        console.error('err', err);
      });

    },
    handleSubmit(data) {
        updateCourse(this.state.courses.id, data);

        //TODO figure out why window.location redirect works, but router and browserhistory does not
        //this.props.router.push('/courses');
        window.location.href = '/courses';
        /*
        browserHistory.push({
            pathname: '/courses'
        });
        */
    },
    render() {
        return (
            <div>
                <CourseForm onSubmit={this.handleSubmit}
                    course_number={this.state.courses.course_number}
                    department={this.state.courses.department}
                />
            </div>
        );
    }

API 调用:

export function fetchCourses() {
    console.log("Fetching courses");
    return fetch(Config.apiBaseUrl+'/courses', {
        method: 'GET',
        mode: 'cors',
        headers: {
            'Accept': 'application/json',
            'Cache-Control': 'no-cache',
            'Authorization': 'Bearer '+auth.getToken(),
        },
        cache: 'no-cache'
    }).then(res => res.json())
        .catch(err => err);
}

export function updateCourse(id, data){
    return fetch(Config.apiBaseUrl+'/courses/'+id, {
        method: 'PATCH',
        mode: 'cors',
        body: JSON.stringify(data),
        headers: {
            'Accept': 'application/json',
            'Authorization': 'Bearer '+auth.getToken(),
            'Content-Type' : 'application/json'
        }
    }).then(res => {
        return res;
    }).catch(err => err);
}
4

1 回答 1

3

乍一看,您似乎正在使用(组件)本地状态,而您应该使用应用程序状态。

this.setState(...)incourseUpdate不会更新对应的课程courseList。这与单页应用程序相关,尤其是在导航期间未卸载组件时(例如,我在上面的评论中提到的内容)。

有两种方法可以做到这一点:

1 -将状态提升到一个共同的父级。这可能是解决此问题的最简单方法。一个例子是:

class ContainerComponent {
    updateItem = (item, newData) => {
        updateTheItem(item, newData).then((updatedItem) => {
            /* replace the item in state with updatedItem */
        });
    }

    componentDidMount() {
        fetchItems().then(/* store items in state */);
    }

    render() {
        const { items } = this.state;

        return (
            <div>
                <List items={ items } onItemSelect={(item) => this.setState({ selectedItem: item })}>
                <Detail item={ this.state.selectedItem } updateItem={ this.updateItem }>
            </div>
        )
    }
}

详细地说,您无需在此处更新项目,而是调用props.updateItem,这将在父项中更新它,并同步两个子项(两者ListDetails)。

2 - 我认为您正在寻找类似的东西redux(也许还有react-redux绑定)。状态将由单个存储管理,组件将一致地从中读取。如果这将是一个大型应用程序,我建议沿着这条路线走——如果没有帮助,管理许多不同组件之间的共享状态可能会变得很麻烦。

于 2017-03-15T21:58:45.503 回答