Реагировать на кеширование моих запросов 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 привязок). Состояние будет управляться одним хранилищем, и компоненты будут считывать его последовательно. Если это будет большое приложение, я бы предложил пойти по этому пути - управление общим состоянием между различными компонентами может стать проблематичным, если у вас нет помощи.

Другие вопросы по тегам