State and Forms in React, Part 3: Handling the Complex State
First two articles explained the basics of the value link pattern and Volicon’s Value Link library, showing you how to make simple forms with React. Now it’s time to learn something exciting. Thing is, that value link pattern is not just about stupid forms. It can help you to build an application. And in this tutorial, we’re going to build one.
So, that’s an application. Let’s see what we’ve got from our previous examples. We already have the code for the edit user form, which we will take from previous tutorial, and we already knows how it works. But it operates on its internal state, and we need to convert it to be reusable widget first. Then, we will need the component for the list itself, and we will need to wire it all together.
Good news is that it’s way easier than you think, especially if you have prior experience with React. Let’s start with our familiar add user control.
At this point, I really suggest you reading previous tutorials if you didn’t. We’re about to explore very uncommon React programming technique in its full power, so it’s really important to be fluent with its basics. Also, some basic knowledge of React is required.
Making reusable Edit User widget
<EditUser/> will needs to notify upper component that any of the Save or Cancel buttons are pressed, so parent can close the dialog. That part is easy. We need to pass onClose() callback to it in props. Tricky part is that in case if Save button is pressed it needs to modify user object in the way, that parent will notice the change.
Which, in turn, can be done with an additional callback, but we hate additional callbacks. Thus, we will pass the link to user object in props instead. And when Save button is pressed, we will update the link with the new object.
Before that — no changes should be applied, which means that we still need the local state to hold intermediate user input. So, when form will be opened, we set the state from the link; when form is submitted, we update the link with our state. And, when form should be closed for any reason, we fire onClose() callback.
Taking it all together, our <EditUser/> form component starts looking like this:
Now some of you who are familiar with React will inevitably remember that “assigning props to state is an antipattern” . Relax :). Most of the time it’s true. But here is the good example of the situation when you’re really need to do it.
Great. We have reusable component with “edit user” form. It’s time to think about our application. How will we handle the users list, and how we will create the link to the particular user?
Let’s start writing our root application component from the simple structure without behaviour, which we can easily understand. First, it will need a state, holding the list of users. Which, obviously, will be an array.
We render an array with Array.map function, and for the purpose of clarity, we move grid row and header markup out of the render() to the separate components.
Our header and row will look something like on this picture on the left.
That’s pretty obvious stuff for anyone, who learnt React basics.
Just note, that we prefer stateless components as functions when there is no state. Always do like this. Zero boilerplate “custom tag” definitions is one of the greatest thing React can give you.
Now having the structure, lets add some simple behavior to the <UserRow/>:
- It need to tell the parent, that Edit button is pressed, so it can open the dialog. Which is just calling the callback from props on click.
- It need to flip user.isActive on click in the way that parent will notice.
- It need to delete user in the way that parent will notice.
It order to make it happen, we can either add two more callbacks and let parent do it, and it’s “the usual way”, or… pass the link to the user object instead of object itself.
Damn, that link to the user object again. So, how can it be done?
Link to the user object
Essentially, we need to generate link to user object while we’re iterating through the users array with map(). And I wouldn’t offer it to you if it wouldn’t be dead simple. So, meet the new Volicon Value Link library method. link.map().
If you have a link which enclosed value is an object or array, you can iterate through the links to its members using link.map().
That’s easy. So, we need just to take the link to the users array instead of accessing it directly, and then apply the same map() method to it. And we know how to take links to the state members, aren’t we?
Now, something interesting should happen inside of UserRow. First, we need to flip user.isActive when this field is clicked. It would be trivial, if we could take the link to user.isActive, and certainly, we can.
If you have a link with an enclosed object or array, link.at( key ) method creates the link to object member with a given key.
Having the link to it, isActive could be flipped with isActiveLink.set( !isActiveLink.value ). Which, in turn, will change the user object, which will change usersLink, which will change the <UsersList/> state, and our parent component will update and redraw anything. And that’s what we want.
isActiveLink.set( !isActiveLink.value ) — too many letters. Let's make it a bit more elegant. We have something else in inventory.
link.update( prevValue => nextValue ) updates the link using the given value transform function.
If we will use link.update(), our code will become a bit cleaner.
link.action( ( prevValue, e )=> nextValue ) creates an event handler, which will transform the link value in the way like link.update() does.
With link.action(), we can omit an arrow function. So our code once again becomes a bit shorter:
And the last thing we need, is to delete the user. Do we have anything special for that? Yes, we do.
linkToArrayOrObjectElement.remove() will remove an element from the array or object.
Now we’ve got everything we need to finish with <UserRow/>. Our final code looks like this:
And we’re almost done. The last thing we have to do, is to wire up our users list with user edit dialog.
Edit User Dialog
We will need <EditUser/> component to be displayed in a pop-up window. Since writing pop-up widgets is not an objective of this tutorial, we will take an existing implementation.
The first one we can found. This one looks okay.
Let’s start with the editing of our user. First, because the dialog has isOpen state which must controlled from outside, we need to add it to our root component state. And we also need to know, which user we’re editing. Thus, user id (which will be just an index of the user in the array) needs to be added to our state too.
In order to open the dialog, all we need to do is to update the root component state. This is the React way. We will wrap this state manipulations to dedicated methods, though. To make it look a bit better, than plain React way. And our code with <EditUser/> dialog starts looking like this:
Note, how we’re taking the link to the user object. We’re already knows what link.at( key ) method does, so it’s not a problem.
Now we have the last thing left to do — the Add User dialog.
Add User Dialog
Thing is that it’s technically the same dialog. It just takes an empty object to edit, and when it’s saved, it should push new user object to the users array, not update an existing object in place.
Which we will do assembling the custom link for this specific dialog.
Link.value( value, set : x => void ) creates custom link object with a given value and set function.
Now, we will just literally describe the behavior we need in our custom link.
And we’re done.
Congratulations to everyone who are reading this, now you’re understand Value Links! To the degree which will really help you to build an applications, not just simple forms.
In the next article, we will discuss how to handle different data binding scenarios. Say, radio groups.