What is an immutable state?
It's a state that doesn't change over time.
So when we create an immutable object we don't change it's properties, If we need to change it's properties, we create a new copy of it and modify the properties as we want.
In JavaScript we can use both mutable and immutable states, We decide which is better for us, We can use Object.freeze() for example to make our object immutable.
Let's have an example, We've an object called state ...
const state = {
id: 1,
name: 'John',
accounts: {
facebook: {
id: 'facebook',
url: '/'
},
twitter: {
id: 'twitter',
url: '/'
}
}
};
We need to modify it's twitter account url and assign it to a new state without affecting the current state, So we would do something like this
const newState = {
...state,
accounts: { ...state.accounts, twitter: {
...state.accounts.twitter, url: '/newUrl' } }
};
This code has two main issues
- It's so long in comparison to its purpose, I just want to update one property.
- Readability is not so good, Why I'm seeing many properties and nested levels while I'm only updating one property?!
If we use ImmerJs, It'll be like this
const newState = produce(state, (draftState) => {
draftState.accounts.twitter.url = '/newUrl';
});
Here, we can see
- Shorter code
- More readable, I'm only seeing the property I'm changing!
So, Immerjs produce function creates a new draft copy of your object or array, And let you modify it as you want, Then returns you a new state with the changes you applied.
In React
All states are immutable, When we set a state we create a new variable and assign it to the state, We don't change the current one
Let's have an example
const [quiz, setQuiz] = useState({
title: "Quiz 1",
questions: [
{
id: "1",
type: "mcq",
title: "Question 1?",
answers: [
{
id: "a_1",
text: "Answer 1",
isTrue: true
},
{
id: "a_2",
text: "Answer 2",
isTrue: false
}
]
}
]
});
We've a state holds an object, What if we want to push a new answer to the quiz? We would do something like this
const addAnswer = (questionId, newAnswer) => {
setQuiz({
...quiz,
questions: quiz.questions.map((question) =>
question.id === questionId
? { ...question, answers: [...question.answers,
newAnswer] }
: question
)
});
};
Complex, right? Here's how it's done using Immerjs
const addAnswer = (questionId, newAnswer) => {
setQuiz(produce(prev, (draft) => {
const question = draft.questions.find((el) => el.id === questionId);
question.answers.push(newAnswer);
}
);
};
So it's not just about reducing amount of code, But we only focus on what we need to change "The answer", without caring about the other quiz properties.
There's also a light package use-immer contains a custom hook useImmer, It's used instead of useState to call the produce function automatically when setting a state
const [quiz, setQuiz] = useImmer({
.....
});
It'll make our code more simpler
const addAnswer = (questionId, newAnswer) => {
setQuiz((draft) => {
const question = draft.questions.find((el) => el.id === questionId);
question.answers.push(newAnswer);
});
};
This awesome package immerjs makes Redux toolkit too much simpler than old redux pattern, It's automatically implemented there, So you don't need to use the many spread operators just to change one value in your reducers.
Thank you so much!
Top comments (0)