如何从数组对象中的超链接获取数据并 return 结果?

How to fetch data from a hyperlink in in an object of arrays and return the result?

我的后端returns以下数据。对于某些数据,我需要获取一个超链接才能获取数据。虽然这是针对单个对象的,但非常简单。我很纠结如何从对象数组中获取数据。

 {
      count: 2,
      next: null,
      previous: null,
      results: [
        {
          id: 3,
          title: 'Story-2',
          description: 'Law window student scientist between news receive. Sign require point.',
          price: '0.00',
          author: '23f1a20asdfsa9-8175-4aasdfdsaf54-9a42-27fesadfdsaff258fc86',
          place: 3,
          placeUrl: 'http://localhost:8000/api/v1/places/3/',
          imagesUrl: 'http://localhost:8000/api/v1/story-images/?place=3',
          titleImage: 'http://localhost:8000/media/story_media/example_jJiJGMr.jpg'
        },
        {
          id: 4,
          title: 'Test',
          description: 'sdfdsdsfdsf',
          price: '0.08',
          author: '23f1a209-817sdfdsf5-4a54-9a42-27fef25sadfds8fc86',
          place: 1,
          placeUrl: 'http://localhost:8000/api/v1/places/1/',
          imagesUrl: 'http://localhost:8000/api/v1/story-images/?place=4',
          titleImage: 'http://localhost:8000/api/v1/stories/?limit=9&offset=0&author=23f1a209-8175-4a54-9a42-27fef258fc86'
        }
      ]
    }

我现在想访问 PlaceUrl 的数据(例如,包含名称、城市等)并将数据添加到原始对象。我目前的做法是这样的。不过感觉不对:

const selectStoriesList = (state) => state.story.storyList.results;

export const selectStoriesListView = createSelector(
  selectStoriesList,
  (selectStoriesList) => {
    let preparedStories = [];
    if (selectStoriesList && selectStoriesList.length > 0) {
      const promises = selectStoriesList.map(async (item) => {
        const place = await axios.get(item.placeUrl, tokenConfig());
        return {
          authorUsername: item.author,
          authorNickname: "",
          authorImage: "",
          titleImage: item.titleImage,
          title: item.title,
          place: place.data.name,
          city: place.data.city,
          linkToProfile: "",
          linkToPlace: "",
          linkToStory: `www.ssdfsuy`,
          storyPrice: item.price,
          storyIsBought: "",
        };
      });
      console.log(promises);
      let preparedStories = Promise.all(promises).then((values) => {
        console.log(values);
        return values;
      });

      return Promise.all(promises);

      // return Promise.all(promises);
    } else {
      return preparedStories;
    }
  }
);

获取调用需要什么样子?欢迎任何提示。

A selector 不是执行异步抓取的地方。它应该是来自您商店的简单 selection 数据。

您在这里处理的是两个实体类型之间的关系:storyplace。您需要将数据存储在您的商店 in a normalized way, possibly using createEntityAdapter 中,以便您可以通过其 ID 查找地点。

您可以通过多种方式触发获取地点:

在组件中

Return 只是 select 报道时的地点 ID。您的故事组件将呈现一个地点组件,例如 <Place id={3}/>Place 组件将 select 来自商店的地点对象,并使用 useEffect 钩子到 dispatch 其 ID 的“请求地点”操作。

在 Thunk

payload creator函数的createAsyncThunk action which you use to fetch the stories can dispatch additional actions through the thunkAPI argument。或者它可以执行多个 API 请求,并在包含地点和故事的单个操作中 return 数据。

这是最后一个想法的代码,尽管我实际上更喜欢基于组件的方法。

import {
  configureStore,
  createAsyncThunk,
  createEntityAdapter,
  createReducer,
  createSelector
} from "@reduxjs/toolkit";
import axios from "axios";
import { uniq } from "lodash";

const tokenConfig = () => ({});

const placesAdapter = createEntityAdapter();

const storiesAdapter = createEntityAdapter();

export const placeSelectors = placesAdapter.getSelectors(
  (state) => state.place
);

export const storySelectors = storiesAdapter.getSelectors(
  (state) => state.story
);

const fetchStories = createAsyncThunk(
  "fetchStories",
  async (_, { getState }) => {
    // fetch the stories
    const storyRes = await axios.get("/api/v1/stories/", tokenConfig());
    const storyData = storyRes.data;
    // array of place ids for each story
    const placeIds = storyData.results.map((story) => story.place);
    // find which place ids are already in the state and can be skipped over
    const loadedPlaceIds = placeSelectors.selectIds(getState());
    // filter out duplicates and already loaded
    const idsToFetch = uniq(
      placeIds.filter((id) => !loadedPlaceIds.includes(id))
    );
    // fetch all places
    const places = await Promise.all(
      idsToFetch.map((id) => axios.get(`/api/v1/places/${id}/`, tokenConfig()))
    );
    // return your data
    return {
      places, // an array of place objects
      storyData // the story data
    };
  }
);

export const storiesReducer = createReducer(
  storiesAdapter.getInitialState({
    storyList: {
      count: 0,
      next: null,
      previous: null,
      ids: []
    }
  }),
  (builder) =>
    builder.addCase(fetchStories.fulfilled, (state, action) => {
      const data = action.payload.storyData;
      // save all of the stories
      storiesAdapter.upsertMany(state, data.results);
      // save the list, but with just the ids for the stories
      state.storyList = {
        ...data,
        ids: data.results.map((story) => story.id)
      };
    })
);

export const placesReducer = createReducer(
  placesAdapter.getInitialState(),
  (builder) =>
    builder.addCase(fetchStories.fulfilled, (state, action) => {
      // save all of the places
      placesAdapter.upsertMany(state, action.payload.places);
    })
);

export const selectStoriesListView = createSelector(
  // select the list of story ids
  (state) => state.story.storyList.results,
  // select the stories
  (state) => state.story.entities,
  // select the places
  (state) => state.place.entities,
  // combine all
  (ids, stories, places) => {
    // map ids to objects
    const storyObjects = ids.map((id) => stories[id]);
    // reformat
    return storyObjects.map(({ author, titleImage, price, title, place }) => {
      // get the place from id
      const { name, city } = places[place];
      return {
        authorUsername: author,
        authorNickname: "",
        authorImage: "",
        titleImage,
        title,
        place: name,
        city,
        linkToProfile: "",
        linkToPlace: "",
        linkToStory: `www.ssdfsuy`,
        storyPrice: price,
        storyIsBought: ""
      };
    });
  }
);

export const store = configureStore({
  reducer: {
    story: storiesReducer,
    place: placesReducer
  }
});