In ancient times, Zhao Zilong was able to take the head of an enemy general in a group of thousands of troops in the face of the situation of “charge, advance without retreat, battle will, death without life”. In Javascript, we use objects to store a data structure. If the structure is very complex, it is not easy to extract a value safely and elegantly.
This article will elaborate on how to safely complete read and write operations in a deeply nested scenario. I will try a variety of methods successively, hoping to inspire readers.
A. Schroif’s latest article: The Safely login to Deeply Values In JavaScript
The scene is introduced
In React development, we render views based on data. Something like the following often happens:
const props = {
user: {
posts: [{title: 'Foo'.comments: [ 'Good one! '.'Interesting... '] {},title: 'Bar'.comments: [ 'Ok'] {},title: 'Baz'.comments: []}],comments: [...]
}
}Copy the code
This is a typical scenario for taking user review information and presenting it. In fact, this is not nested deep enough, imagine a reply has multiple layers: reply to reply, reply to reply to reply…
Let’s take a look at our example, where we want to get the comment information for the first post. With traditional javascript methods you should do this:
props.user &&
props.user.posts &&
props.user.posts[0] &&
props.user.posts[0].commentsCopy the code
Perhaps experienced javascript developers will see the point of using so much &&. This is the process of validating the existence of each key and index in order to correlate values in the object. Otherwise there will be an error, which can be fatal. The props data structure must be dynamically generated, sometimes valid and sometimes invalid. During testing, it’s hard to replicate.
The same awkward situations abound. Imagine if we needed to get the title of a user’s last comment on a blog:
props.user &&
props.user.comments &&
props.user.comments[0] &&
props.user.comments[0].blog.titleCopy the code
Are these examples exaggerated? It’s not. We see that to get a data value, we need to iterate over the existence of the property layer by layer. This is undoubtedly tedious.
The solution
Now that I understand the problem we are facing, I will use several methods:
- Pure JavaScript methods;
- Ramda, the most functional JavaScript library, uses ideas and solutions such as currying to solve the problem.
JavaScript solution
First go directly to the code:
const get = (p, o) = > p.reduce((xs, x) = > (xs && xs[x]) ? xs[x] : null, o)
console.log(get(['user'.'posts'.0.'comments'], props)) // [ 'Good one!', 'Interesting...' ]
console.log(get(['user'.'post'.0.'comments'], props)) // nullCopy the code
Note that I use a more functional reduce approach in ES5. As for this method, I think many people do not understand it, so I suggest you learn it first or refer to my previous article.
At the same time, I try to get: user->posts[0]->comments, with a counter example: user->post[0]->comments; Of course, in the counter example, the POST array does not exist.
Let’s take a look at the code.
const get = (p, o) = >
p.reduce((xs, x) = >
(xs && xs[x]) ? xs[x] : null, o)Copy the code
In the get method we implemented, we receive two parameters. The first p represents the path to obtain the value. The other parameter represents the target object.
Again, for more flexibility and abstraction in design. We can cornetize our method:
const get = p= > o =>
p.reduce((xs, x) = >
(xs && xs[x]) ? xs[x] : null, o)Copy the code
In this case, you can call this pose:
const getUserComments = get(['user'.'posts'.0.'comments'])
console.log(getUserComments(props))
// [ 'Good one!', 'Interesting...' ]
console.log(getUserComments({user: {posts: []}}))
// nullCopy the code
If the use of reduce in the GET method is unclear, let’s look at a simple example:
['id'].reduce((xs, x) = > (xs && xs[x]) ? xs[x] : null, {id: 10})
/ / return 10Copy the code
Ramda scheme
Instead of designing the above methods ourselves, we can use the Ramda functional library to do this:
const getUserComments = R.path(['user'.'posts'.0.'comments'])Copy the code
The next call requires this pose:
getUserComments(props) // [ 'Good one!', 'Interesting...' ]
getUserComments({}) // nullCopy the code
What if we want to return custom content instead of null if a value is not found in the specified path? We can use the pathOr method, where the first parameter is used to set the default output.
const getUserComments = R.pathOr([], ['user'.'posts'.0.'comments'])
getUserComments(props) // [ 'Good one!', 'Interesting...' ]
getUserComments({}) / / []Copy the code
conclusion
This article is translated from A.Sharif’s latest article: The Safely login Deeply Values In JavaScript. The latter part actually analyzes the implementation of Ramda+Folktale and Ramda+Lenses.
Folktale and Lenses are very Functional Programming ideas that are relatively obscure and private to understand. Interested readers can click on the original to find out for themselves.
Happy Coding!
If you’re not a fan of functional programming, just learn the first part of the implementation. For those of you who are interested in functional programming, I hope this article will inspire you to communicate with me.
PS: author Github warehouse, welcome to communicate through various forms of code.