Как хранить данные из API, чтобы они были доступны в других компонентах

Я делаю проект с React.js и Wordpress REST API. Все это время я использовал axios для получения данных из API всякий раз, когда компонент монтируется. Это означает, что каждый раз, когда я визуализирую компонент, который должен отображать некоторые данные, я делаю запрос API. Проект масштабируется, и я понял, что такой способ ведения дел не самый лучший.

Как мне справиться с этой проблемой? Должен ли я использовать Redux? Я думаю, что Redux — это излишество для этого, так как я не буду изменять данные, я буду только отображать их. Должен ли я использовать React Context API?

Если какой-то код нужен, я добавлю.


person Gvidas Pranauskas    schedule 17.11.2019    source источник
comment
Сам React позволяет вам управлять состоянием, используя либо компоненты класса, либо useState. Итак, все, что вам действительно нужно сделать, это иметь какое-то состояние, живущее у общего предка любого компонента, которому нужны данные API, и хранить данные там, передавая любую необходимую информацию в качестве реквизита.   -  person Nick    schedule 17.11.2019
comment
Но что, если я использую React Router для навигации по моему приложению?   -  person Gvidas Pranauskas    schedule 17.11.2019
comment
Это не должно быть проблемой, вы все равно можете передавать данные маршрутам с помощью реквизитов рендеринга: tylermcginnis.com/react-router-pass-props-to-components   -  person Nick    schedule 17.11.2019


Ответы (2)


Я думаю, что redux — лучшее решение для обеспечения масштабируемости и хорошей организации вашего проекта, но если вы не хотите его использовать, вы можете попробовать с контекстом, имейте в виду, что это означает, что вы должны вызывать свой API в высокоуровневом компоненте. .

Вот пример компонента вашего приложения с определением контекста:

import React, { useState, useEffect } from 'react';
import axios from 'axios';

export const DataContext = React.createContext({})

const App = () => {
  const [apiResponse, setApiResponse] = useState({});

  useEffect(() => {
    axios.get(endPoint).then(response => setApiResponse(response))
  },[]);

  return (
    <DataContext.Provider value={apiResponse}>
      <YourAppContent />
    </DataContext.Provider>
  );
}

export default App

Каждый раз, когда вам нужно получить данные API:

import React, { useContext } from 'react';
import Card from './Card';
import { DataContext } from '../App';

const UserCard = () => {
  const data = useContext(DataContext);
  const { user } = data || {};
  return <Card>{user.fullName}</Card>
}

export default UserCard
person vlk    schedule 17.11.2019
comment
Спасибо за ответы! Я собираюсь использовать Context API. - person Gvidas Pranauskas; 17.11.2019

Если у вас относительно простое состояние приложения, и вы хотите отображать только данные. Я думаю, что использование Context API является подходящим подходом.

Для простого состояния

import React, { useState, useEffect, useContext, createContext } from 'react';
// some sort of libs that fetchs data for you.
import FetchService from './util/FetchService';

const OrderInfoContext = createContext();

// This is a helper component that generates the Provider wrapper
function OrderInfoProvider(props) {
  // We will persist API payload in the state so we can assign it to the Context
  const [orders, setOrders] = useState([]);


  // We use useEffect to make API calls.
  useEffect(() => {
    async function fetchData() {
      // This is just a helper to fetch data from endpoints. It can be done using axios or similar libraries
      const orders = await FetchService
        .get('/v1/registry/orders');
      setOrders(orders);
    }
    fetchData();
  });
  //we create a global object that is avaibvale to every child components 
  return <OrderInfoContext.Provider value={[orders, setOrders]} {...props} />;
}

// Helper function to get Context
function useOrderInfo() {
  const context = useContext(OrderInfoContext);
  if (!context) {
    throw new Error('useOrderInfo must be used within a OrderInfoProvider');
  }
  return context;
}

export { OrderInfoProvider, useOrderInfo };

Для слегка сложного состояния

Поскольку ваше приложение становится немного сложнее. Вы все еще можете использовать тот же подход с хуком useReducer

// reducer returns the next state based on the action passed in
const reducer = (state, action) => {
  switch (action.type) {
    case INITIAL:
      return [];
    case FETCH_ORDER:
      return action.payload;
    case ADD_ORDER:
      return [...state, action.payload];
    default:
      throw new Error();
  }
};

// This is a helper component that generate the Provider wrapper
function OrderInfoProvider(props) {
  // We will persist API payload in the state so we can assign it to the Context
  const [orders, dispatch] = useReducer(reducer, []);
  const addOrderInfo = (data) => { addOrders(dispatch, data); };

  async function fetchOrders() {
    const data = await ApiService
      .get('/v1/registry/orders');
    dispatch({
      type: FETCH_ORDER,
      payload: data
    });
  }

  useEffect(() => {
    fetchOrders();
  }, []);

  // we create a global object that is available to every child components
  return <OrderInfoContext.Provider value={[orders, dispatch]} {...props} />;
}

Для сложного состояния

Вам обязательно следует рассмотреть возможность использования Redux, как предложил другой ответ. Инструмент разработчика Redux может сэкономить вам много времени на отладку.

Справочник

Я взял код из пары статей, которые написал несколько раз назад. Вероятно, вы можете найти лучшее объяснение там.

https://tech.zola.com/practical-use-cases-of-react-context-and-hooks-a9f62815e38d

https://www.andrew-zheng.com/blog/use-reducer

person Andrew Zheng    schedule 17.11.2019
comment
Спасибо за ответы! Я собираюсь использовать Context API. - person Gvidas Pranauskas; 17.11.2019