2017-03-15 75 views
2

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

过程:

首先,我去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

是否同时显示'CourseList'和'CourseUpdate'(即在主 - 从布局中)?如果是这样,当您“离开”导航时不会再调用componentDidMount(反应路由器Link不会导致浏览器中的导航事件)'courseUpdate' –

回答

3

乍一看,它看起来像你使用(组件)您应该使用应用程序状态的本地状态。

this.setState(...)courseUpdate将不会更新courseList中的相应课程。这与singlepage应用程序相关,特别是在导航过程中组件未被卸载时(例如,我在上面的评论中提到的内容)。

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

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,这将在父更新,并实现同步的两个孩子(包括ListDetails)。

2 - 我想你正在寻找像redux(也许与react-redux绑定)。状态将由单个商店进行管理,并且组件会一致地读取它。如果这将是一个大型应用程序,我会建议走这条路线 - 如果你没有帮助,管理许多不同组件之间的共享状态可能会变得毛骨悚然。