Using recoil.js in react, in class component without using hooks
Asked Answered
N

3

7

I am new to recoil, and I am using all class components. Here is my recoil state

export const tokenState = atom({
  key: "tokenState",
  default: "",
});

How to use recoil in class component and set the token? I have used RecoilRoot in app as

<RecoilRoot>
  <Header />
  <Main />
</RecoilRoot>

In login.js, I want to set the token in recoil, but login.js is a class component.

const res = await APIS.login(apiRoute,requestObject);

In res.data.token I am getting the jwt token.

Thank you in advance!

Northnorthwest answered 12/7, 2020 at 11:8 Comment(2)
Bikram, is there anything wrong with Matt's or mine answers? Do you need any more info?Outhouse
Works pretty well. Thanks a lotNorthnorthwest
P
9

Recoil.JS is meant to be used with react hooks, I don't think they provide any other functions other than hooks. If you can't change Login.js to a functional component try using a wrapping functional component that passes the token as prop to the login.js component.

I'd suggest useRecoilState(myAtom).

function LoginWrapper(props) {
    const [token, setToken] = useRecoilState(myAtom);

    useEffect(() => {
      async function get() {
         const { data: { token: jwt } } = await APIS.login(apiRoute,requestObject);

         setToken(jwt);
      }

      get();
    }, []);

    return <LoginComponent {...props} jwt={token} />
}
Phototelegraphy answered 12/7, 2020 at 14:35 Comment(0)
O
8

As @Matt said, you must wrap your class component into a functional one that passes the Recoil APIs as a prop.

Anyway, if you want to set the atom' token from your Login component, you could simply wrap Login into a component that passes down the result of useSetRecoilState

const LoginWrapper = () => {
  const setToken = useSetRecoilState(tokenState);
  return <Login setToken={setToken} />;
};

In case of more complex cases, you could leverage Recoil' useRecoilCallback that allows you to consume the whole Recoil APIs at once

const LoginWrapper = () => {
  const setToken = useRecoilCallback(({ set }) => token => {
    set(tokenState, token);
  });

  return <Login setToken={setToken} />;
};

Please note: the main difference between using setRecoilState and setRecoilCallback is that using setRecoilState automatically subscribes the consuming component to the atom changes, while setRecoilCallback does not subscribe it so you could avoid some renders.

You can find both the solutions in this working CodeSandbox. As you can see the PrintToken component re-renders correctly as soon as the Login component updates the token.

Let me know if you need more help 😊

Outhouse answered 17/7, 2020 at 7:32 Comment(0)
C
0

Solution 1:

call RecoilNexus at root file as App.tsx or index.tsx

return (
<RecoilNexus />
)

then, you can call getRecoil for get recoil state and setRecoil for set recoil state

import {setRecoil, getRecoil, getRecoilPromise, resetRecoil} from '@components/RecoilNexus';

export const tokenState = atom({
  key: "tokenState",
  default: "",
});


const onSetTokenState=setRecoil(tokenState)
onSetTokenState("abc")


const tokenRecoilState=getRecoil(tokenState)
console.log(tokenRecoilState)


RecoilNexus.tsx

import {RecoilValue, RecoilState, useRecoilCallback} from 'recoil';

interface Nexus {
  get?: <T>(atom: RecoilValue<T>) => T;
  getPromise?: <T>(atom: RecoilValue<T>) => Promise<T>;
  set?: <T>(
    atom: RecoilState<T>,
    valOrUpdater: T | ((currVal: T) => T),
  ) => void;
  reset?: (atom: RecoilState<any>) => void;
}

const nexus: Nexus = {};

export default function RecoilNexus() {
  nexus.get = useRecoilCallback<[atom: RecoilValue<any>], any>(
    ({snapshot}) =>
      function <T>(atom: RecoilValue<T>) {
        return snapshot.getLoadable(atom).contents;
      },
    [],
  );

  nexus.getPromise = useRecoilCallback<[atom: RecoilValue<any>], Promise<any>>(
    ({snapshot}) =>
      function <T>(atom: RecoilValue<T>) {
        return snapshot.getPromise(atom);
      },
    [],
  );

  nexus.set = useRecoilCallback(({set}) => set, []);

  nexus.reset = useRecoilCallback(({reset}) => reset, []);

  return null;
}

export function getRecoil<T>(atom: RecoilValue<T>): T {
  return nexus.get!(atom);
}

export function getRecoilPromise<T>(atom: RecoilValue<T>): Promise<T> {
  return nexus.getPromise!(atom);
}

export function setRecoil<T>(
  atom: RecoilState<T>,
  valOrUpdater: T | ((currVal: T) => T),
) {
  nexus.set!(atom, valOrUpdater);
}

export function resetRecoil(atom: RecoilState<any>) {
  nexus.reset!(atom);
}

Solution 2:

Wrap the Function component around the Class component and pass the recoil state or setter function as a prop like this

function LoginCompnentWrapper(props) {
    const [token, setToken] = useRecoilState(myAtom);
    return <LoginComponent {...props} token={token} setToken={setToken} />
}
Coleslaw answered 17/1, 2023 at 10:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.