36
loading...
This website collects cookies to deliver better user experience
Recoil works and thinks like React!
const fontSizeState = atom({
key: 'fontSizeState',
default: 14,
});
useRecoilState()
. It's just like React's useState()
, but now the state can be shared between components:const [fontSize, setFontSize] = useRecoilState(fontSizeState);
const fontSizeLabelState = selector({
key: 'fontSizeLabelState',
get: ({ get }) => {
const fontSize = get(fontSizeState);
const unit = 'px';
return `${fontSize}${unit}`;
},
});
useRecoilValue()
, which takes an atom or selector as an argument and returns the corresponding value.const fontSizeLabel = useRecoilValue(fontSizeLabelState);
yarn add recoil
or npm install recoil
package.json
file as "recoil": "^0.3.1"
RecoilRoot
to appear somewhere in the parent tree. A good place to put this is in your root component (App.js
). I created and exported a separate RecoilApp
component to be able to wrap my app with the RecoilRoot
export default function RecoilApp() {
return (
<RecoilRoot>
<App />
</RecoilRoot>
);
}
atoms/Players.js
, let's create our players state using the atom function.import { atom } from 'recoil';
import players from '../assets/players';
export const allPlayersState = atom({
key: 'allPlayersState',
default: players,
});
useRecoilValue()
function to retrieve the value of the state.// App.js
import { useRecoilValue, RecoilRoot } from 'recoil';
import { allPlayersState } from './atoms/Players';
function App() {
const players = useRecoilValue(allPlayersState);
return (
...
)
}
// atoms/Players.js
export const positionFilterState = atom({
key: 'positionFilterState',
default: [],
});
component/Filter.js
using the useRecoilState
. This is very similar to the normal useState
from React.// components/Filter.js
import { useRecoilState } from "recoil";
import { positionFilterState } from "../atoms/Players";
const Filters = () => {
const [positionFilter, setPositionFilter] =
useRecoilState(positionFilterState);
const onFilterPress = (position) => {
setPositionFilter((curPositionFilter) => {
if (curPositionFilter.includes(position)) {
return curPositionFilter.filter((pos) => pos !== position);
} else {
return [...curPositionFilter, position];
}
});
}
const isSelected = (position) => {
return positionFilter.includes(position);
}
return (
...
)
allPlayersState
and positionFilterState
.// atoms/Players.js
import { atom, selector } from 'recoil';
export const filteredPlayers = selector({
key: 'filteredPlayers',
get: ({ get }) => {
const players = get(allPlayersState);
const filters = get(positionFilterState);
return players.filter(
(player) => filters.length === 0 || filters.includes(player.position),
);
},
});
allPlayersState
atom with the filteredPlayers
selector in the App.js to display the filtered players in the list.// App.js
import { allPlayersState, filteredPlayers } from './atoms/Players';
function App() {
const players = useRecoilValue(filteredPlayers);
...
}
MyPlayersState
. This is going to be an array of players.MyPlayersState
atom, which is an array, and will return the players grouped by their position. This will help us display them on the field.// atoms/MyTeam.js
import { atom, selector } from 'recoil';
export const myFormationState = atom({
key: 'myFormation',
default: {
FWD: 3,
MID: 3,
DEF: 4,
GCK: 1,
},
});
export const myPlayersState = atom({
key: 'MyPlayersState',
default: [],
});
const positions = ['FWD', 'MID', 'DEF', 'GCK'];
export const myPlayersByPosition = selector({
key: 'myPlayersByPosition',
get: ({ get }) => {
const players = get(myPlayersState);
const formation = get(myFormationState);
const groupedPlayers = {};
positions.forEach((position) => {
groupedPlayers[position] = players.filter((p) => p.position === position);
// fill with null values, up to the amount of expected players from formation
for (
let i = groupedPlayers[position].length;
i < formation[position];
i++
) {
groupedPlayers[position].push(null);
}
});
return groupedPlayers;
},
});
PlayerListItem
component that renders one player in the list of ALL players.const [myPlayers, setMyPlayers] = useRecoilState(myPlayersState);
onPress
function, that will modify the state accordingly. If the clicked player is already in my team, then we will want to remove it from the state. Otherwise, we want to add it to the team, but before adding it, we should also make sure there is an empty spot for the player in the team. For example, if we press on a Defender, and our formation is 3-3-4, meaning that we can have a maximum of 4 defenders, we will be able to add the player only if there are fewer than 4 defenders already selected.View
of the component with a Pressable
and attach the onPress
event. Also, by adding this conditional style { backgroundColor: isSelected ? '#d170db' : 'white' },
to the Pressable
, the row will become purple if the player is selected.// components/PlayerListItem.js
import { useRecoilState, useRecoilValue } from 'recoil';
import { myFormationState, myPlayersState } from '../atoms/MyTeam';
const PlayerListItem = ({ player }) => {
const [myPlayers, setMyPlayers] = useRecoilState(myPlayersState);
const myFormation = useRecoilValue(myFormationState);
const numberOfPlayersOnPos = myPlayers.filter(
(p) => p.position === player.position,
).length;
const onPress = () => {
setMyPlayers((curPlayers) => {
if (curPlayers.some((p) => p.id === player.id)) {
return curPlayers.filter((p) => p.id !== player.id);
}
// CHECK if it's possible to add
if (numberOfPlayersOnPos < myFormation[player.position]) {
return [...curPlayers, player];
}
return curPlayers;
});
};
const isSelected = myPlayers.some((p) => p.id === player.id);
return (
<Pressable
onPress={onPress}
style={[
styles.container,
{ backgroundColor: isSelected ? '#d170db' : 'white' },
]}
>
...
</Pressable>
);
};
components/Field.js
file and will replace the local players
dummy object with the value of myPlayersByPosition
selector.// components/Field.js
import { useRecoilValue } from "recoil";
import { myPlayersByPosition } from "../atoms/MyTeam";
const Field = () => {
const players = useRecoilValue(myPlayersByPosition);
return (
...
);
};
FieldPlayer.js
, line 22.{
player ? player.name : position;
}
atoms/MyTeam.js
// atoms/MyTeam.js
export const numberOfPlayers = selector({
key: 'numberOfPlayers',
get: ({ get }) => {
return get(myPlayersState).length;
},
});
export const valueOfPlayers = selector({
key: 'valueOfPlayers',
get: ({ get }) => {
return get(myPlayersState).reduce((acc, player) => acc + player.price, 0);
},
});
components/TeamStats
component. We will display the number of players in the team, and the remaining budget ($100m - total value of the players);// components/TeamStats.js
import { useRecoilValue } from "recoil";
import { numberOfPlayers, valueOfPlayers } from "../atoms/MyTeam";
const TeamStats = () => {
const nofPlayers = useRecoilValue(numberOfPlayers);
const value = useRecoilValue(valueOfPlayers);
return (
...
<Text style={styles.value}>{nofPlayers} / 15</Text>
...
...
<Text style={styles.value}>
${((100_000_000 - value) / 1_000_000).toFixed(1)}m
</Text>
...
);
};