The key to being productive with Immutable JS is understanding how to update values that are nested. Using
setIn you can place a new value directly into an existing or new path. If you need access to the previous value before setting the new one, you can use
updateIn accepts the same path lookups as
setIn, but gives you a callback function instead so that you can use the previous value however you wish and return an updated version.
SetIn takes an array as its first parameter. These are the paths. We'll say home, loading. The second parameter is the value you want to set. In our case, we want to set true. If we logged the updated state, we can see now that the loading property has been set to true.
What if you want to toggle this value instead? That's a bit more tricky because you need to know the previous value before you can set the next one. You can do this by getting the current value first.
If we say that the current is equal to getIn instead of setIn, and because we're dealing with a Boolean value here, we can flip it, we can say it's the opposite of current, there you can see we get the same result. All of this duplication is not looking good. Surely, there's a better way. You can change setIn to be updateIn instead.
UpdateIn takes the same path lookup as the first parameter, but you get to provide a callback. Inside this callback, X will now be equal to the current value of this path lookup. Whatever you return from this function will be the new value that is set. This is ideal for us because we want to flip over whatever the loading property of a set is.
If we log it again, you can see it still works. This time, we have no duplication of these path lookups. Everything is contained in this single bit of code. UpdateIn gets even more powerful when you begin to use it with lists.
If we add a messages array to our original data, and we'll populate it with a single message that has a type of info and a message of "Welcome to our website." If you wanted to allow a user to clear all messages, you need some way of resetting this value here to be empty. Just as before, you could actually say setIn and then provide the path, so home, messages.
Because we're going to clear all messages, we don't care what's there. We could just provide a new list. If we log that to the console, you can see that that's had the desired effect. We now have an empty list of messages.
Let's say that the messages array could also contain some errors. We'll copy this. We'll say one has a type of error, "Your email has not been validated." If you want to allow the user to clear only messages that have that type of error and leave behind this one, that would require us to filter these messages before we sent them again.
We could say filtered is equal to state.getIn home, messages. We can call filter and only accept those items where the type is not equal to an error. This will be a list that only contains this item. We could set it again here. When we log it, you see we get the desired results. Messages has one item in it as of type info.
Just like before, there's a much better way to do this. We can copy this callback function. Get rid of all of that, change this to updateIn, and then provide a callback which will get the messages. We can return the same filter we had before. We'll say msgs.filter. When we log it, you see the exact same result, but this is much cleaner.
You should keep in mind that, when using updateIn, whatever you return from this function will be set as the new value of this path. You need to be careful not to mix and match your types. For example, if you accidentally return a Boolean here, log this to the console, you can see that Messages is now true.
If someone else later was trying to, for example, loop over those messages to display them on their page, they'll most likely run into an error because you can't loop over the value of true. That's one of the negatives to look out for.
The positives outweigh it because, as well as filtering, you could also do something such as push another item onto that messages array simply by calling push and proving a new map that has the type info and a message. Log this to the console, let's just get the home, and then say toJS to make this a bit more readable.
You can see now that we have three messages, the original two and the one we just pushed onto it there.
One final example to drive this home. Let's go back to having a single message. Let's say for some reason you know that you'll want to update the message text on the very first message. Back in the update in callback, we have access to all the messages. Because this is a list, we can get the very first one. Then we can set the message to be a new message.
Log that to the console. You can see that we do get a new message. That's pretty cool. If you think about it, this is actually a case where you know the message you want to send and you know for a fact it's that first message in the array.
UpdateIn, in this case, is not the appropriate method. We should use setIn. How can we get into the first message then the message property? These path lookups accept indexes also.
You can simply say zero here and then the message. We're going to run out of space on screen. We're going to set that to new message. Again, if we log that, you can see we have the new message. In this case, setIn was the correct method to use because we knew what value we wanted to set, we knew where we wanted to set it, and we didn't need access to the original to be able to set it.