Skip to content
This repository was archived by the owner on Jan 1, 2025. It is now read-only.

add actions to atoms as second optional parameter #1496

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

MFrozenM
Copy link

@MFrozenM MFrozenM commented Dec 12, 2021

function counterStore() {
  return atom(
    {
      key: 'counterAtom',
      default: 0,
    },
    {
      setCounter: counter => {
        return counter;
      },
      increaseCounter: state => {
        return state + 1;
      },
      decreaseCounter: state => {
        return state - 1;
      },
      add: function (state, value) {
        return state + value;
      },
      addThree: (state, value, s_value) => {
        return state + value + s_value;
      },
    },
  );
}

function CounterComponent() {
  const increaseCounter= useRecoilAction(counterStore, 'increaseCounter');

const clicked = () => {
  increaseCounter()
}

  return (
    <div onClick={clicked}>
      
    <div/>
  );
}

I have made a change in atoms API. There are actions that have been added to atoms as a second optional parameter which can make the code more predictable and easy to reason about by knowing which functions can change the state. useRecoilAction hook does not make any effects on other hooks.
Another benefit is you don't need to pass state in actions to trigger and the action will pass the state to the function whenever called.

There is increaseCounter function in the atom actions:

 increaseCounter: state => {
        return state + 1;
      }

You can call it like:

const increaseCounterAction = useRecoilAction(counterStore, 'increaseCounter');
increaseCounterAction()

And another action for increasing the counter by adding it to another value:

 increaseCounterByValue: (state, value) => {
        return state + value;
      }

You may call it like:

const increaseCounterByValueAction = useRecoilAction(counterStore, 'increaseCounterByValue');
increaseCounterByValueAction(15)
// If the default value of the state was five, then the new state will be 20 .
// ( prevState: 5, value to add to state: 15 => state + value = 20 )

You just have to pass the value and not the state to the function increaseCounterByValueAction.

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Dec 12, 2021
@drarmstr
Copy link
Contributor

Thanks for contributing! Though, can you clarify what this new functionality enables that isn't possible with hooks like useRecoilCallback() and useRecoilTransaction()? Ideally we would like to keep the surface area of the core API as minimal as possible. They can be used for reducer patterns with actions similar to this (see this example). Their definition can also be placed next to the atom in the file, to achieve co-location of state and logic if desired without requiring to mix them if not. Another concern is that we want the difference between atoms and selectors to be abstract from the perspective of the users from component hooks or downstream selectors. You'll note there is not a type to enforce hooks are only used with atoms or to provide static type safety for string-based action names. But, I think this PR demonstrates the potential for an implementation to be layered on top of the core API for a simpler interface to implement the reducer pattern.

@MFrozenM MFrozenM closed this Dec 22, 2021
@MFrozenM MFrozenM reopened this Dec 22, 2021
@MFrozenM
Copy link
Author

useRecoilTransaction() is not a pure function and we have effects when we set a new value in the reducer as the doc. So, we can not isolate and track side effects as usual. Another thing is we can use key pairs instead of untidy switch case and reducer.

{
      increaseCounter: state => {
           return state + 1;
      },
      decreaseCounter: state => {
           return state - 1;
      }
}

As you say to keep the atom area as minimal as possible, we are able to stick a set of actions to an atom in another way and as I mentioned declaring actions are completely optional:

function counterStore() {
  return atom(
    {
      key: 'counterAtom',
      default: 0,
    }
}

setActions(counterAtom, {
      increaseCounter: state => {
           return state + 1;
      },
      decreaseCounter: state => {
           return state - 1;
      }
})

const CounterAction = useRecoilAction(counterStore);
CounterAction.decreaseCounter()
CounterAction.increaseCounter()

In this way, we can easily write pure functions without any side effects and the setAction can be on the top level and outside of the core API. However, The final decision is up to you.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants