反应缓存我的API请求

时间:2017-03-15 21:27:54

标签: reactjs react-router isomorphic-fetch-api

我遇到这个奇怪的问题,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);
}

1 个答案:

答案 0 :(得分:3)

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

this.setState(...)中的

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

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

1 - Lifting state up为共同的父母。这可能是解决此问题的最简单方法。一个例子是:

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,这将在父级中更新它,并同步两个孩子(List和{{1} })。

2 - 我认为您正在寻找类似Details(可能还有redux绑定)的内容。 State将由单个商店管理,组件将始终从中读取。如果它将成为一个大型应用程序,我建议沿着这条路走下去 - 如果你没有帮助,管理许多不同组件之间的共享状态可能会变得毛茸茸。