Weekly Webtips
Published in

Weekly Webtips

Dynamically Highlighting Text in React Native

Or… How I learned to love React FlowBox.

Photo by Bailey Heedick on Unsplash

This one should have been easy — or so I thought. I had a block of text, and I needed to highlight text in that block. I figured I’d whip something together, and be done in a few minutes. Then, I started WAY overthinking things.

Let’s walk through all the things I did wrong. :)

First — here’s my stated output:

Actually, ‘var1’ is a clickable pressable box.

The text came from this:

“text”: “Carefully, ‘{var var1}’ you say to the Grue! “

This is a JSON file containing the data I’m reading in. It could just as easily be a REST service or other item.

I’ve defined a pseudo-grammar to allow me to specify variables and other actions in the text, which allow me to control the program flow, none of which is the point of this article.

What I want to do is to highlight the var1 in that sentence, ensuring that it stands out to the user.

This sentence, if it was not dynamic, could be composed of the following:

And that would have been all well and good.

Except….

We need a dynamic method of generating this.

Our text is read in, parsed, and then displayed. This allows us to fill in variables, user effects, and other items from a text definition file.

Let’s start with the first part — breaking the text apart.

After looking into this, there are multiple ways — straight parsing, reg-ex, etc.

I decided to go with the prewritten regexify-string: https://www.npmjs.com/package/regexify-string

This allowed me to write code like:

(Yes, I know that line 5 — where I slice off the ‘{var …} and leave just the value name, needs to change for future iterations. For now, ignore it, please? :) )

This takes a simple RegEx and returns a new component, with a certain key and text. (In this case, the text is everything but the {var and }.

This returns an array with multiple entries in it — separated by the text we wanted to see:

Okay- great! That is 2 strings and a React Node. Just what we wanted.

First, I naively tried to just display the result.

Classic — BZZZT wrong.

Next, tried wrapping it in a <Text> block:

return <Text>{textToDisplayArray}</Text>;

This resulted in … something that looked like what I wanted:

Something seems off….

Something seemed off though. I was trying to see why the highlighted text was off, it wasn’t lined up.

I called up Flipper for this:

Our ‘var1’ group

Our var1 text has… a height of 53. Something else is odd though — this text item is a child of a different view group than our other text.

Our other text group

This text group is a height of 64, and appears to be anchored differently than the var1 group.

Another clue — looking at the text for this textview, we can see something interesting: “Carefully, ‘0’, you say to the Grue!’.

Where did that 0 come from?

Here, I used Flipper’s ability to edit on the fly — something that’s pretty useful:

Yeah — that doesn’t look right.
Editing on the fly!

So, where is this 0 coming from?

By default, when presented with an Array, React will attempt to concatenate it all together and display it.

Notice what happens if we make an array of just text strings:

There is only 1 React node now, and it has our array (3 slots: slot1, slot2, slot3) in a single React Text node.

If we add an object to this array — for every object we add, we get a new ‘0’, as the concatenation runs into a NULL and converts it to 0.

And here’s… how I ran off the rails. :)

First, I looked back at my simple example:

And decided that I needed to build a string with the components in it.

So… insert code like this:

textArray.forEach((item) => {
textList += item;
}

That went about as well as you might think — what this tries to do is to create a new string with all the items specified. Except… this is a string, and the string combines an object (the JSX specifier) and a String, so the entire thing is pretty much gobblety gook.

React certainly tried though — mostly due to Javascript and its inherent flexibility to handle objects.

Carefully, '[object Object]' you say to the Grue!

Still, not what we wanted —

Okay — so now that I was thinking a bit more clearly, I referenced my earlier static string and decided maybe I needed to embed it in a Text component so that everything was rendered correctly inline.

This parses the entire array and creates a new array, although every object is a JSX object:

Nope. Still not what I’m looking for.

Same issue as before.

At this point, I really went into the weeds — looking into how to concatenate JSX elements. Suffice it to say, there are some really neat answers out there:

I spent a while studying these… but they both felt overly complex — like I was missing something.

I then briefly walked through React Fragments.

React Fragments are a way to return bits of JSX from a function, in a way that a higher-order component can use them.

For instance, a function can return a TR\TD setup from a function — one for each row. By itself the TR\TD isn’t valid — we would normally have to wrap it in a <div> — (or View in React Native), but then the Table isn’t valid.

So a React Fragment is a way around that.

Still, that would require reordering my code, and again, made the code feel inefficient and incorrect. So… it just didn’t sit right with me.

Then, I realized something. React Native is based around FlexBox, and FlexBox has something called… a row flex.

This is where the child components are rendered in a way that is not the default. The default is top to bottom. What I needed was left to right, wrapping around the screen.

We can see that our ending text is part of a ReactTextView, a peer of the other TextView. That’s a good start. The ReactViewGroup (our Pressable) has 1 and exactly 1 ReactTextView as a child.

All of this looks right.

So, in the end, we made a list of the objects we want to display…. and then display them in row order, instead of column order:

See? Way, way overthought this here. :)

The only thing left to do is to give the items a key — otherwise React complains at us. I have 2 kids, I don’t need more complaining.

This is easy — in our textList fat arrow, let’s add ‘key={index + 5000}’ to this.

That gives our original regular expression the first 4000 entries for found regular expressions… and the rest of our text everything after 4000.

And, with that… we’re done!

--

--

--

Explore the world of web technologies through a series of tutorials

Recommended from Medium

Fixing some minor bugs

How to setup personal blog using Ghost and Github hosting

Learning JS: Equality in Javascript

Be careful with JavaScript getters and setters

Blog Article 03 — NodeJS

extending hyper — ->

Codable Coredata MVVM

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Allan Graves

Allan Graves

Years of technology experience have given me a unique perspective on many things, including parenting, climate change, etc. Or maybe I’m just opinionated.

More from Medium

State management in React with redux vanilla

ReactJS VS React Native: Key Differences

React JS