Реагировать на кеширование моих запросов 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 ответ
На первый взгляд кажется, что вы используете (компонентное) локальное состояние, в котором вы должны использовать состояние приложения.
this.setState(...)
в courseUpdate
не будет обновлять соответствующий курс в 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
, который будет обновлять его в родительском и синхронизировать двух детей (оба List
а также Details
).
2 - Я думаю, что вы ищете что-то вроде redux
(и, возможно, с react-redux
привязок). Состояние будет управляться одним хранилищем, и компоненты будут считывать его последовательно. Если это будет большое приложение, я бы предложил пойти по этому пути - управление общим состоянием между различными компонентами может стать проблематичным, если у вас нет помощи.