Использование собственного restClient в саге
Я хочу выбрать все локали из моего API (API / локали).
Моя проблема в том, что вызов yield () возвращает функцию в виде строки плана (да, да, в виде простой строки), и я понятия не имею, почему!
Может быть, я что-то пропустил с откликом yield + call + restClient?? знак равно
Мой файл саги выглядит так:
import types from './types';
import actions from './actions';
import { call, put, takeEvery } from 'redux-saga/effects';
import restClient from './../../restClient';
function* getLocalesSaga() {
try {
yield put({type: types.GET_LOCALES_LOADING});
let locales;
locales = yield call(restClient, 'GET', 'locales');
console.log(locales); // this show the function string!!!
if (!locales.data) {
throw new Error('REST response must contain a data key');
}
yield put( {type: types.LOCALES_RECEIVED, locales } )
} catch (error) {
console.log(error);
yield put({type: types.GET_LOCALES_FAILURE, error})
}
}
export default function* localesSaga() {
yield [
takeEvery(types.GET_LOCALES, getLocalesSaga),
takeEvery(types.GET_LOCALES_LOADING, actions.loadingLocales),
takeEvery(types.LOCALES_RECEIVED, actions.localesReceived),
takeEvery(types.GET_LOCALES_FAILURE, actions.failedLocales),
];
}
Вывод console.log:
ƒ (type, resource, params) {
if (type === __WEBPACK_IMPORTED_MODULE_0_admin_on_rest__["GET_MANY"]) {
return Promise.all(params.ids.map(function (id) {
return httpClient(apiUrl + '/' +…
Нет запроса на вкладке сети браузера. Нет ошибок JS в консоли, кроме console.log.
Я зарегистрировал сагу с customSagas={customsSagas} в компоненте Admin.
Когда я использую функцию fetch(), она работает!
Я хочу использовать мой restClient, который включает токен аутентификации и всю логику запроса и ответа. RestClient является пользовательским, и это код:
import {
GET_LIST,
GET_ONE,
GET_MANY,
GET_MANY_REFERENCE,
CREATE,
UPDATE,
DELETE,
fetchUtils
} from 'admin-on-rest';
const { queryParameters, fetchJson } = fetchUtils;
const apiUrl = process.env.REACT_APP_API_PATH;
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' });
}
const token = localStorage.getItem('token');
options.headers.set('Authorization', `Bearer ${token}`);
return fetchJson(url, options);
}
/**
* Maps admin-on-rest queries to a json-server powered REST API
*
* @see https://github.com/typicode/json-server
* @example
* GET_LIST => GET http://my.api.url/posts?_sort=title&_order=ASC&_start=0&_end=24
* GET_ONE => GET http://my.api.url/posts/123
* GET_MANY => GET http://my.api.url/posts/123, GET http://my.api.url/posts/456, GET http://my.api.url/posts/789
* UPDATE => PUT http://my.api.url/posts/123
* CREATE => POST http://my.api.url/posts/123
* DELETE => DELETE http://my.api.url/posts/123
*/
export default () => {
/**
* @param {String} type One of the constants appearing at the top if this file, e.g. 'UPDATE'
* @param {String} resource Name of the resource to fetch, e.g. 'posts'
* @param {Object} params The REST request params, depending on the type
* @returns {Object} { url, options } The HTTP request parameters
*/
const convertRESTRequestToHTTP = (type, resource, params) => {
let url = '';
const options = {};
switch (type) {
case GET_LIST: {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
...params.filter,
sort: field,
order: order,
page: page,
per_page: perPage,
};
url = `${apiUrl}/${resource}?${queryParameters(query)}`;
break;
}
case GET_ONE:
url = `${apiUrl}/${resource}/${params.id}`;
break;
case GET_MANY_REFERENCE: {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
...params.filter,
[params.target]: params.id,
_sort: field,
_order: order,
_start: (page - 1) * perPage,
_end: page * perPage,
};
url = `${apiUrl}/${resource}?${queryParameters(query)}`;
break;
}
case UPDATE:
url = `${apiUrl}/${resource}/${params.id}`;
options.method = 'PUT';
options.body = JSON.stringify(params.data);
break;
case CREATE:
url = `${apiUrl}/${resource}`;
options.method = 'POST';
options.body = JSON.stringify(params.data);
break;
case DELETE:
url = `${apiUrl}/${resource}/${params.id}`;
options.method = 'DELETE';
break;
default:
throw new Error(`Unsupported fetch action type ${type}`);
}
return { url, options };
};
/**
* @param {Object} response HTTP response from fetch()
* @param {String} type One of the constants appearing at the top if this file, e.g. 'UPDATE'
* @param {String} resource Name of the resource to fetch, e.g. 'posts'
* @param {Object} params The REST request params, depending on the type
* @returns {Object} REST response
*/
const convertHTTPResponseToREST = (response, type, resource, params) => {
const { headers, json } = response;
switch (type) {
case GET_LIST:
case GET_MANY_REFERENCE:
if (!headers.has('x-total-count')) {
throw new Error('The X-Total-Count header is missing in the HTTP Response. The jsonServer REST client expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare X-Total-Count in the Access-Control-Expose-Headers header?');
}
return {
// change the primary key to uuid
data: json.data.map(resource => resource = { ...resource, id: resource.uuid }),
total: parseInt(headers.get('x-total-count').split('/').pop(), 10),
};
case UPDATE:
case DELETE:
case GET_ONE:
return { data: json, id: json.uuid };
case CREATE:
return { data: { ...params.data, id: json.uuid } };
default:
return { data: json };
}
};
/**
* @param {string} type Request type, e.g GET_LIST
* @param {string} resource Resource name, e.g. "posts"
* @param {Object} payload Request parameters. Depends on the request type
* @returns {Promise} the Promise for a REST response
*/
return (type, resource, params) => {
if (type === GET_MANY) {
return Promise.all(params.ids.map(id => httpClient(`${apiUrl}/${resource}/${id}`)))
.then(responses => ({ data: responses.map(response => response.json) }));
}
const { url, options } = convertRESTRequestToHTTP(type, resource, params);
return httpClient(url, options)
.then(response => convertHTTPResponseToREST(response, type, resource, params));
};
};
Любой может помочь здесь и сказать мне, почему restClient возвращается как строка, а не возвращает json?
Это не запрос GET_LIST или GET_ONE. Это просто обычный запрос GET. Я пытался использовать GET_ONE и GET_LIST, но я все еще получаю ответ в виде простой строки функции.
Редактировать и решение:
Благодаря @Gildas для GET-запроса стало проще использовать fetch вместо restClient. restClient используется только для <resource />
и это не так ясно по документам. Более того, действия создателей были бесполезны, когда я использую put
в моей главной функции саги.
Моя выборка выглядит так, и это работает:
function getLocales() {
return fetch(process.env.REACT_APP_API_PATH + '/locales', { method: 'GET' })
.then(response => (
Promise.resolve(response)
))
.then(response => (
response.json()
))
.catch((e) => {
console.error(e);
});
}
Когда я назвал это так:
const { languages, currentLocale } = yield call(getLocales);
Заранее благодарю. Лео.
1 ответ
Эта часть действительно странная, кажется, вы связываете создателей действий, как если бы они были сагами, хотя вы делаете put
их в getLocalesSaga:
export default function* localesSaga() {
yield [
takeEvery(types.GET_LOCALES, getLocalesSaga),
takeEvery(types.GET_LOCALES_LOADING, actions.loadingLocales),
takeEvery(types.LOCALES_RECEIVED, actions.localesReceived),
takeEvery(types.GET_LOCALES_FAILURE, actions.failedLocales),
];
}
Кроме того, restClient
не является fetch
, GET
это не тип, распознаваемый им (см. документацию). Вы не должны использовать restClient
для всего, что не является ресурсом с точки зрения администратора на отдыхе. Здесь вы действительно должны использовать fetch
,
Это, вероятно, следует переписать так:
import types from './types';
import actions from './actions';
import { call, put, takeEvery } from 'redux-saga/effects';
import restClient from './../../restClient';
function fetchLocales() {
return fetch(...);
}
function* getLocalesSaga() {
try {
yield put(actions.loadingLocales());
let locales;
locales = yield call(fetchLocales);
console.log(locales); // this show the function string!!!
if (!locales.data) {
throw new Error('REST response must contain a data key');
}
yield put(actions.localesReceived(locales))
} catch (error) {
console.log(error);
yield put(actions.failedLocales(error))
}
}
export default function* localesSaga() {
yield takeEvery(types.GET_LOCALES, getLocalesSaga);
}