简体   繁体   中英

Is it correct or good practice to use SWR together with Redux?

I'm using the SWR

In my code I am using the junction of both as follows

const vehiclesStates = useSelector(({ vehiclesStates: state }) => state); // REDUX
const response = useFetch("/vehicles/states") // SWR
const dispatch = useDispatch(); // REDUX

// REDUX
useEffect(() => {
  if(response.data) dispatch(VehiclesStatesActions.setVehiclesStates(response.data.result))
}, [response.data, dispatch]);

if(response.error) return <div>ERROR - {response.error.toString()}</div> // SWR
else if(!response.data) return <div>LOADING</div> // SWR

// If there is a return I render my component
return (<></>)

In this excerpt above I set my component state in redux with the data that I return using the SWR (Integrated with axios). That way, in any other component that needs to use that same data, I only import it with redux's useSelect .

This way

const vehiclesStates = useSelector(({ vehiclesStates: state }) => state);

I find it more practical than just using the SWR by calling useFetch on each component that wants to use that data.

My question is: Is using this way a correct way, or can it affect performance or something?

Thanks in advance

I think that's a little redundant, you could be better using one or the other, SWR is intended to use the cache as a local state, because that state lives on the server it's more adecuated and frees you from managing every manipulation in the state verbosely, like you have to do with redux explicitly calling API calls anytime you dispatch an action.

Using both kinds of defeat the purpose of SWR, you'll be better just using Redux then. A good rule to follow is to use SWR to manage the state that lives in the server, and Redux to manage the local state.

Some scenario where I can see both coexisting, it's when you have a very complex data manipulation, and you have to modify and delete information too often, I think that case will use some mix of redux and the mutate function of SWR.

You can use swr with redux and that is not redundant. Because there are 2 types of state. UI State and Server Caching State . An example of a Ui state is if the modal is on or off. Server Caching State, we call api, store the result and cache that result for use in the client side. swr is handling the Server Caching State.

In the early days of redux, there was no caching. Then some libraries emerged, in next.js swr, in react react-query libraries. Redux-toolkit has its own api caching implementation.

I think with React-16, we got context api. React context itself is not a state management tool. It is just a mean to transfer the data. It becomes a state management tool if you use with useState or useReducer . Combining React Context with your own hooks, you can replace redux. And in your hook functions you should use swr . I explained here how to combine react-context,hooks and swr to create a full state management tool: Global state managment and Next.js

A response from the backend may have a list of items. The SWR approach will force you to redraw all elements that use items from the list or to create a set of equality comparers and/or contexts that smart enough to tell where the part of the DOM should be updated or not.

Redux is also need some efforts for stability of props. Yet one can get away with provided useSelector + shallowEqual to extract just minimum needed. That is list of id's for the List component and row by id in the card component.

Please review the code. IMHO the two methods are not direct competitors.

Having said that, remember that there are libraries (incidentally, Material-UI) that manage lists for you. If you use one of those use their recommendations to optimize redraws (if you need to).

    import { FC } from "react";
    import { shallowEqual, useSelector } from "react-redux";
    import useSWR from "swr"

    interface CardInfo {
      id: number;
      name: string;
      price: number;
    };
    type RootState = Array<CardInfo>;

    declare const fetcher: () => Promise<CardInfo[]>

    const CardView: FC<CardInfo> = (p) => (<pre>{JSON.stringify(p)}</pre>);

    const CardById: FC<Pick<CardInfo, "id">> = ({ id }) => {
      const card = useSelector((rootState: RootState) => rootState.find((c) => c.id === id), shallowEqual)
      if (typeof card === "undefined") {
        return null;
      }
      return <CardView {...card} />
    }

    export const ListSwr: FC = () => {
      const { data } = useSWR(
        "/api/cards",
        fetcher,
        /*{compare: NO FANCY COMPARE HELPS 
          WHEN ONLY ONE OF MANY ITEMS CHANGED}*/
      );
      if (!Array.isArray(data)) {
        return null;
      }
      return (<>
        {data.map((c) => (<CardView key={c.id} {...c} />))}
      </>)
    }

    export const ListFromStore: FC = () => {
      const ids = useSelector((rootState: RootState) => rootState.map(e => e.id), shallowEqual)
      return (<>
        {ids.map((id) => <CardById key={id} id={id} />)}
      </>)
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM