Switch between different branches to see usage examples.
import { useState } from "react";
const [value, setValue] = useState(0); // default value
handleChange = () => {
setValue(value + 1); // takes a function that returns new value
};
- Allows components to share state without having to pass the data as props all the time.
- Helps avoid props drilling.
const CounterContext = createContext();
const CounterProvider = ({ children }) => {
const [numberOfClicks, setNumberOfClicks] = useState(0);
const increment = (amount) => {
setNumberOfClicks(numberOfClicks + amount);
};
return (
<CounterContext.Provider value={{ numberOfClicks, increment }}>
{children}
</CounterContext.Provider>
);
};
- Wrap the necessary components in the provider.
<CounterProvider>
<div className="app">...</div>
</CounterProvider>
- Then, access the data in any of those components using useContext hook.
const { numberOfClicks, increment } = useContext(CounterContext);
Recoil has two main concepts: atoms and selectors.
- Atoms are individual values that we want to store in the Recoil state.
- Selectors take the fundamental values expressed as atoms and transform them in some way or combine them into another value.
Note: All the components within recoil will share this state. It is useful in many scenarios but could be problematic in others.
npm install recoil
- Atoms are functions that we can use to create new pieces of the Recoil state.
const counterState = atom({
key: "counterState",
default: 0,
});
Wrap the components in <RecoilRoot>
and then access the state in any of those components using:
useRecoilValue
hook: to access a particular state value.
const numberOfClicks = useRecoilValue(counterState);
useRecoilState
hook: to access and modify a particular state value.
const [numberOfClicks, setNumberOfClicks] = useRecoilState(counterState);
Selectors allow us to define certain logic in one spot. Instead of defining the logic in multiple components, we can use a selector instead.
const numberOfClicksSelector = selector({
key: "numberOfClicksSelector",
get: ({ get }) => {
// get currrent data from a recoil state
const clicksData = get(counterState);
// return a modified value
const numberOfClicks = clicksData.reduce((sum, clickData) => {
return sum + clickData.amount;
}, 0);
return numberOfClicks;
},
});
Access the modified value in any of those components using useRecoilValue
hook.
npm install redux react-redux
- Any action that can potentially change the state of our application.
- Eg: when user clicks on a button, when data finishes loading, when data starts loading.
- It contains:
type
of action andpayload
export const counterButtonClicked = {
type: "COUNTER_BUTTON_CLICKED",
payload: { amount: 1 },
};
- An action creator is a function that takes in any values and returns an action according to said values.
export const counterButtonClicked = (amount) => ({
type: "COUNTER_BUTTON_CLICKED",
payload: { amount: amount },
});
- Reducers tell redux how the state of our application should change whenever any given action occurs.
- Takes in:
current state
andaction
. - Returns:
new state
after action.
export const numberOfClicksReducer = (state = 0, action) => {
const { type } = action;
switch (type) {
case "COUNTER_BUTTON_CLICKED":
return state + action.payload.amount;
default:
return state;
}
};
- It generally involves a switch function for type of action involved.
- Default case returns state as it is, i.e. no change.
- Allow components to accurately get data out of the state and occasionally to transform data of the state.
export const numberOfClicksSelector = (state) => state.numberOfClicks;
- Combine reducers using
combineReducers
and create a redux store using therootReducer
.
const rootReducer = combineReducers({
numberOfClicks: numberOfClicksReducer,
});
const store = createStore(rootReducer);
- Get values from state using
useSelector
hook with the corresponding selector.
const numberOfClicks = useSelector(numberOfClicksSelector);
- Tell redux that an action should get dispatched using
useDispatch
hook with the corresponding action.
const dispatch = useDispatch();
...
<button onClick={() => dispatch(counterButtonClicked)}>Click Me</button>
MobX takes a more object-oriented approach as compared to redux.
npm install mobx mobx-react-lite
- Use a class to store the state.
- The class can contain values and actions.
class Counter {
value = 0;
constructor() {
makeObservable(this, {
value: observable,
increment: action,
});
}
increment = (amount) => {
this.value += amount;
};
}
- Pass an instance of that class to components.
const counter = new Counter();
<DisplayCount counter={counter} />
<CounterButton counter={counter} />
- use
makeObservable
ormakeAutoObservable
in the class constructor
constructor() {
makeObservable(this, {
value: observable,
increment: action,
});
}
or
constructor() {
makeAutoObservable(this)
}
- wrap your components in
observer
const CounterButton = observer(({ counter }) => {
...
});
Since we are passing the class instance to components through props, it can again result in props drilling.
We can use Context API with MobX to make the class instance available to all components.