Back to Top

How to Create a Content Strategy for COVID-19 (or Other Crises) in Five Steps

If this pandemic has blindsided your business, you’ve been asking questions like these: How should we talk to our prospects and customers? Should we ditch our editorial plans, or stay the course? Is content promotion still acceptable, or is it a definite no-no? You need a content strategy for this … Read the full article at MarketingProfs

Reblogged 1 year ago from www.marketingprofs.com

36 Account-Based Marketing Stats to Know in 2020

As marketers, we’ve become experts in our target audiences. We know their typical demographic, what they like, the platforms they spend time on, and what they value in a product.

But unlike sales reps — who talk to and learn from our biggest prospects each day — we don’t always know tiny, but crucial, details about the brands or people we most want to sell too. This might be why only 28% of salespeople say marketing is their best source of leads.

So, how do we bridge the gap to better help our sales teams sell to high-priority clients? The answer to this question could be account-based marketing (or ABM).

Rather than just marketing to a broad target audience and hoping someone calls your sales reps, ABM is a strategy where sales and marketing teams align from the start to create campaigns that cater to their most qualified leads and current customers.

For example, as part of an ABM strategy, sales reps might offer marketing teams data from customers, prospects, and those that didn’t buy a product so that the marketers can create a targeted campaign that amplifies why their product is valuable and how it can help targets navigate daily pain points.

While inbound marketing is vital for pulling general audiences and demographics to your site, service, or store, account-based marketing can help you to reel in prospects or qualified leads that are most likely to buy your product.

To show you just how beneficial account-based marketing can be, while also informing you of the challenges it comes with, here are 36 stats you should know in 2020.

36 Account-Based Marketing Stats to Know in 2020

General ABM Stats

  • 67% of brands leverage account-based marketing. (HubSpot)
  • “Researching Accounts” and “Identifying Target Contacts” are the top two tactics used by marketers within an ABM model. (HubSpot)
  • Just one year ago, the top five account-based tactics were sales development rep outreach, digital advertising, direct mail, marketing email, and events. (TOPO)
  • Additionally, marketers strongly agree that personalized content (56%) and advanced data management (43%) are keys to ABM’s success. (Forrester)
  • Most organizations intentionally only pursue 38% of their target accounts at one time. (TOPO)
  • By 2019, 55% of marketers rated their ABM strategy as “established” compared to just 43% in 2018. (Forrester)
  • Companies dedicated 29% of their marketing budgets to ABM in 2019. (Engagio)
  • It’s estimated that ABM budgets saw an average increase of 41% in 2019. (TOPO)
  • 57% of professionals say their companies target 1,000 accounts or under with ABM. (TOPO)

Source

  • At the beginning of 2018, only 18% of adopters had two years of experience in account-based marketing. (TOPO)
  • In 2018, 18% of account-based programs were run against current customers (larger organizations with more than $1 billion in revenue ran about 50% of these programs. (TOPO)
  • The most common metric marketers use to track ABM is revenue won. (HubSpot)
  • Two other metrics marketers most commonly track to measure ABM efforts are the number of pipelines created and account engagement. (Engagio)
  • In 2019, 7% of marketers reported a decrease in dedicated headcount for ABM teams, showing that this strategy is going mainstream and being implemented within regular processes. (Forrester)
  • In 2019, 40% of the average marketing team was dedicated to ABM. (Engagio)
  • 57% of marketers say their organizations have implemented ABM for more than one year. (TOPO)
average maturity of account based marketing tactics

Source

Benefits and Challenges of ABM

  • Marketing and sales teams that take an ABM approach together can be up to six percent more likely to exceed their revenue goals than teams less ABM-advanced. (Forrester)
  • 62% of marketers say they can measure a positive impact since adopting ABM. (Forrester)
  • 80% of marketers say ABM improves customer lifetime values, while 86% say it improves win rates. (TOPO)
  • 87% of account-based marketers say that ABM initiatives outperform other marketing investments. (ITSMA)
  • One in five accounts targeted through ABM becomes considered a qualified sales opportunity. (TOPO)
  • Organizations with a strong Ideal Customer Profile (ICP) — which is similar to a buyer persona — achieve 68% higher account win rates. (TOPO)
  • More than two-thirds (69%) of top-performing account-based organizations now have a dedicated account based leader. (TOPO)
  • 70% of companies that started account-based initiatives in the first six months of 2019 have dedicated ABM leaders. (TOPO)
  • 42% of companies that missed their account-based objectives don’t have dedicated leadership. (TOPO)
  • The most common challenge with ABM is delivering a personalized experience. (HubSpot)
  • The top three challenges of implementing ABM are creating a scalable strategy for multiple accounts, producing customized content, and coordinating programs across marketing, sales development, and sales teams. (TOPO)
biggest account based marketing tactics

Source

ABM Technology

  • By 2021, over half of all sales phone calls will be analyzed by software. (TOPO)
  • The conversational intelligence market was estimated to grow by 3x in 2019. (TOPO)
  • Voice assistants will account for at least half of automated data entry by mid-2020. (TOPO)
  • An estimated 55% of sales data entry was eliminated by automation by 2020. (TOPO)

Smarketing Benefits and Challenges

  • Organizations with tightly aligned sales and marketing teams experience 36% higher customer retention rates. (MarketingProfs)
  • The most common measurement of success for content marketing programs is Total Sales. (HubSpot)
  • 85% of marketers with an SLA think their marketing strategy is effective. (HubSpot)
  • Only 7% of salespeople said leads they received from marketing were very high quality. (HubSpot)
  • Only 28% of salespeople said marketing was their best source of leads (HubSpot)

Creating an Effective ABM Strategy

As a marketer, it’s important to know what makes ABM techniques successful and how they differ from other target audience marketing tactics.

Additionally, you’ll want to learn about the companies you’re interested in marketing to, similar to how you learn about a target audience. For example, before creating an ABM campaign, you’ll want to learn about the pain points of companies in your industry, company missions, and what their top decision-makers value in a product or service.

Once you discover valuable data about the accounts you’d like to market to and feel ready to create a campaign, check out our Ultimate Guide to Account-Based Marketing.

Reblogged 1 year ago from blog.hubspot.com

Social Media Marketing Examples of What to Stop, Start, and Keep Doing in 2020

Social media is always shifting and changing, so if you are still running on the same strategy as you have in years past, now is the time to start thinking differently.

The social media landscape today is incredibly different than it was in 2019. Primarily, the global pandemic of 2020 has meant that more people than ever are online.

More people than ever are on social media, and additionally, they’re paying attention in ways that are unprecedented, as suggested by an April 2020 Statista report, which showed that global social media usage has increased by a whopping 44%.

This means there is a lot of opportunity, but also a need for additional caution during a time when emotions are higher and tolerance for brand mess-ups are likely lower. Here, let’s cover what still works when it comes to social media marketing, what’s working better than ever — and what you should leave behind in 2020.

1. First off, pause and re-evaluate your upcoming social media posts.

This is something you already should have done this year, but it’s also something you should keep doing as part of your strategy for any crisis. Take a hard look at what you have scheduled and see if the imagery, the voice and tone, and messaging are right for the current situation. If they aren’t, put those pieces of content on pause until it makes more sense. Then consider how you can revisit a campaign. Hotels.com did a great job with this.

The company needed to change everything in their approach to social media as a result of global bans on travel. The previous Hotel.com ad that they shared in social featuring their mascot, Captain Obvious, contained imagery of people getting close on vacation, a message that would not resonate well in the current climate:

The company quickly pivoted and developed a new video that makes more sense, and shares an important social message:

2. Next, think about how you can shift your messaging to be more helpful or human.

If you work for a brand that is used to a strategy heavily geared toward push marketing, this is the time to start being more human than product-centric. Some companies will be able to do this better than others. If you work for a company that sells yoga pants, your marketing may not need to change as much as a restaurant or an airline. But even then, the best companies aren’t doing the hard sell on their products.

Lululemon is a fantastic example of a company that understands that people need a few more virtual hugs and encouragement than normal, and they acknowledge that in their posts:

3. Adjust your messaging and stop campaigns that could come across as insensitive.

When basketball player Kobe Bryant died in an aerial crash in early 2020, Planters immediately paused their #RIPMrPeanut campaign, which featured Planters’ mascot, Mr. Peanut, falling to his death in an explosion. The company knew the backlash could be strong and halted the campaign to be sensitive to their audience:

Additionally, Google is well-known for its annual April Fool’s jokes, but they realized that this year there might be backlash that plays out in social media over what might seem flippant when much of the world is feeling more stress and fear than normal.

In general, make sure you are paying attention to current events and are ready to pull your posts at a moment’s notice if need be. That’s more important than ever in a climate where emotions are already high.

4. Start placing more emphasis on connection over content.

We are more connected than ever, but for the first time in modern history, as a global society, we’re more apart than ever before as well, with so many people working from home, far from their families, and unable to travel. Even an act as simple as going to the store might be impossible or feel harrowing to people in your audience.

This is a time when pushing content out may be less meaningful for your audience than connecting with them directly.

For example, HubSpot has shifted our Facebook strategy to embrace the method of asking questions that get people talking and thinking:

Additionally, Canva reshares and responds to their customers regularly, winning them even more love for their product:

5. Keep or start being more empathetic — in your messaging, your content, your responses.

In a time when people may feel more vulnerable, it’s important to adjust your brand voice to take that into account. Exploiting a crisis situation is never a good idea. Alternatively, you’ll want to ensure your brand comes across as friendly, helpful, and empathetic.

For instance, sustainable shoe company Allbirds donated shoes to workers on the front-lines of the healthcare crisis. Not only are they directly being helpful, but their messaging and interactions are empathetic and caring:

At a time when many hotels are closed, it’s somewhat hard to stay top of mind. The Sheraton Commander in Cambridge shared a photo on Instagram that is both heartwarming and and full of care:

Coca-Cola took a wildly different tactic altogether. They have a Twitter audience of 3.3 million followers, and are one of the largest brands on the platform. During this time, they’re choosing to avoid posting about their product at all, and are instead resharing tweets that will inform and help people:

6. Stop being spammy.

This goes hand in hand with empathy and engagement. Brands that are using a push strategy for their marketing in social will not have the same results as companies that are taking a more engaging approach.

Be thoughtful of how often you are posting, and where you might have advertising that could be seen as intrusive. Adjust content to fit three main categories: how you can be informing, engaging, or entertaining your customers and fans.

7. Keep up the great social customer service.

Be cognizant of how your audience might be feeling when responding. They may need a bit more care and patience.

Nespresso has made adjustments to their customer care and are responding with well-wishes for customers as part of their customer service efforts:

While the nuances of social media today may be a little different, the foundation of your great social media strategy still remains the same. To learn more, you can take the HubSpot Social Media Certification course, which has been recently updated with new interviews and information to help build the best marketing strategy for your business.

Reblogged 1 year ago from blog.hubspot.com

How to Stay Creative With an SEO-Driven Content Strategy

Posted by Caroline-Forsey

When I first joined HubSpot’s blogging team in January 2018, I loved our writing process. Once a month, we all met in a conference room with a list of ideas on Google Docs which were pitched one-by-one (intricate, I know).

The process was extremely creative, iterative, and collaborative. Of course, it was also often a matter of guess-and-check. Plus, brainstorming can be a bit of a selfish process. The ideas I pitched in those meetings, I pitched in part because I wanted to write them and because I was interested in them as a reader. I could only hope our audience would be interested as well.

While we developed a pulse for understanding what our readers liked from reviewing top viewed posts from the past, our process didn’t enable us to develop content that matched what our potential readers wanted from us.

So, just a few months into 2018, our team pivoted and created a brand new SEO-driven content strategy to address our inability to move forward. Take a look at the organic growth we’ve seen as a result of that strategy over the past two years:

How did we do it? To start, the blogging team partnered with the SEO team. The SEO team now delivers a fresh Search Insights Report (what we’ve come to affectionately call the “SIR”) to us every quarter, which are packed with blog topics vetted for search potential. We diligently move down the list, assigning individual blog topics to be written or updated by writers on the team. From the graph above, you can see the almost immediate growth we expereinced as a result of this new strategy. Within two years, we more than doubled the keywords for which we rank on page one.

As Editor of HubSpot’s Marketing Blog, this left me with a bit of a void. I was thrilled to see the results of the SIRs and recognized how they helped us reach new audiences and rekindle our organic traffic, but, from a personal perspective, I missed the creativity that came with pitching big-risk ideas and watching them pay off. (Believe it or not, articles like “What Is Semi-Structured Data?” wasn’t exactly what I dreamt about publishing when obtaining my English degree.)

However, I’ve learned over the past year that there are ways to remain creative even within a grander, primarily SEO-driven strategy. Here, let’s dive into six tips to ensure you don’t have to sacrifice your own creative freedom for the sake of organic growth.

1. Enlist the help of experts to spark creativity while ensuring posts are still keyword-driven.

A few months ago, I tackled the topic of first versus third party APIs. While I am confident in writing about our product line, “Force quit” is about the extent of my software knowledge (option+command+esc, for those wondering), so I dreaded writing the post. It was both daunting and not particularly inspiring to me as a writer.

Of course, I could’ve written this post the way I’ve written about plenty of other dry topics — by sludging through it, chugging copious amounts of coffee, and listening to Spotify to make it a little more “fun”.

However, when I began writing the post, I wasn’t impressed with my work. Since I didn’t fully grasp the concept, it was surface-level and ambiguous. If a marketer stumbled across it, they wouldn’t learn much.

To solve for this issue, I reached out to a few IT specialists at HubSpot and ended up speaking to two developer support specialists. I even met with one of them via Zoom to further discuss the intricacies of APIs, and recorded the meeting to transcribe later on.

Suddenly, I felt like an investigative reporter. I collected quotes from experts in the field, drafted up a new post that made sense to both myself and the developer support specialists, and published it. I was incredibly proud of the piece because I felt I’d worked as a liaison between the developer world and the marketing world, making the whole concept of APIs a little clearer to my team while ensuring it remained accurate and tactical.

If you’re feeling frustrated by a topic you don’t feel comfortable writing about, don’t hesitate to reach out to experts — even within your own company. Their passion for the subject will fuel your desire to write the piece from a more human angle. Remember, keyword-driven content still leaves plenty of room to angle the piece in a number of interesting directions, as long as the insight you’re providing aligns with the intent of the keyword you’re targeting.

2. Interview leaders in various industries and tell their stories.

Over the past year, I’ve spoken to happiness researcher and speaker Shawn Achor on how happiness leads to success, Harvard professor Amy Edmondson on psychological safety in the workplace, and leadership consultant Simon Hazeldine on using performance psychology to get ahead in the workplace, among many others.

These posts, which enabled me to synthesize complex psychological issues and translate them into tactical strategies for marketers, allowed me to exercise my creative muscle. I interviewed experts via email or on the phone, and used their responses to craft meaningful, coherent narratives. Ultimately, I never felt more “in the flow” than I did when writing these posts.

Your industry undoubtedly has leaders that interest you. If you’re a marketer in the catering or hospitality industries, consider speaking to top chefs in the area. Alternatively, if you’re a marketer for an e-commerce website, try reaching out to e-commerce consultants to get quotes about the future of the industry.

It’s not impossible to align your own interests with business impact, even if those interests are outside the scope of traditional marketing. As someone who’s personally interested in psychology, for instance, I was able to find the intersection between psychology and workplace performance, which helps our readers grow in their own roles.

Including feedback from experts can also give you a competitive advantage in the SERPs. For instance, we published “HubSpot Marketers Give 6 Tips for Fighting Burnout”, on January 20, 2020, and within one month, it already had over 5,000 views. This piece, over time, will likely perform better than a more generic “how to fight burnout” piece without the expert angle.

Ultimately, it’s important to consider who you’re interested in speaking with and how that expert’s experience might align with your audience’s interests, and brainstorm ideas from there.

3. Find the human connection.

As marketers, we’re often tasked with writing about less-than-thrilling topics, particularly if these topics are part of a keyword-driven strategy. For example, take a quick glimpse at some of the pieces we’ve seen on our SIR in the past:

These titles are helpful for our readers, but presenting the information in a creative way becomes difficult. I often tell new writers on the team that you can find an interesting human angle to any topic, no matter how boring it may seem, which makes writing about the topic more exciting and offers more ways for readers to connect with the piece.

The easiest way to find the human angle is to consider the reader’s point of view when searching a topic on Google. Start by asking yourself, “why would I ever search for this topic?”

Searches don’t happen in silos. Nowadays, Google is increasingly trying to continue a “searcher’s journey” through People Also Ask boxes, People Also Search For panels, and Related Search links at the bottom of most SERPs. These features enable searchers to rethink their search and find similar, relevant answers to other questions they might have.

Ultimately, anyone searching for one keyword is searching for that keyword as part of a larger marketing and business strategy. As a content creator, it’s critical you find the bigger picture element and use these new SERP features to tell more creative, holistic stories around the topic at hand.

For instance, recently I wrote a post on how to embed videos in emails. The body of the post itself, I knew, allowed for little creativity — it was essentially a brief step-by-step guide to embedding video. However, I could still find space for creativity in my introduction, and I knew that meant developing empathy for my reader.

I started by imagining the motivation behind any marketer searching “how to embed video in email”. They are likely someone who’s struggling to increase CTR or email subscriptions, so I introduced the topic with a brief, big-picture overview on why email is important for a business’s bottom line (in case you wanted to know, it’s because 87% of businesses use video in their marketing tactics).

Then, I empathize with the reader, acknowledging that sprucing up your emails isn’t always easy, and neither is embedding videos — particularly since major email clients don’t support video embeds.

Suddenly, a topic I’d initially found boring became exciting to me because I could sense the urgency and real-world impact that publishing the piece and answering the reader’s query would have. In essence, what they’re really asking is “How can I continue creating engaging content for my audience?”

That’s a human angle to which I think we can all relate.

4. Use multimedia to freshen up old content.

If you’re struggling with a particularly dry topic, you might evoke creativity by adding multimedia elements like podcasts, YouTube videos, images, or graphs — all of which open up new traffic opportunities since you can generate image traffic through the SERPs as well.

These designs can help you stay engaged when writing the piece, and can also help your post rank on Google, since search engines prefer multimedia components such as images or video.

For instance, we embedded a video in “How to Create An Incredibly Well-Written Executive Summary [+ Example]”. Readers have the option of reading my post, but alternatively, they can watch the discussion take place on-screen.

Of course, multimedia depends on your budget. We aren’t able to add a video to every post we produce. However, there are plenty of simpler forms of multimedia that are free, such as embedded images and graphs.

Additionally, if you’re interested in other aspects of marketing besides writing, this is a good chance to expand your professional portfolio and learn a new skill as well.

5. Frame your content from a unique angle that differentiates it from other search results.

It’s important to note: not all posts need to agree with what’s already on the SERPs for you to rank.

For instance, my colleague Lestraundra wrote “10 Reasons Why You Don’t Need a CRM“. This article currently ranks on page one for the search query “you don’t need a CRM” … but the article actually explains why you do need a CRM, in a playfully sarcastic way.

We managed to rank well while also giving readers something they weren’t expecting. You might consider similar provocative arguments you can make, as the uniqueness (and sometimes controversy) of your writing will enable you to rise up the ranks on the SERPs while providing fresh, interesting content to your audience.

6. Engage with your readers in real life whenever possible.

On one particularly uninspiring day, I set up a 30-minute chat with a customer to learn more about her personal marketing challenges.

As we spoke, I realized how out-of-touch I’d become with some of our readers’ primary struggles. For instance, she was a team of one, which meant while she understood the importance of blogging, she didn’t always have time to develop an in-depth strategy since she was juggling content creation for social media, email marketing, and PR for her small business.

When I got back to my desk, I had no problem writing my assigned post about free social media analytics tools, because I understood the real-world importance of this post for that reader’s daily life. Ultimately, she didn’t have time to research the pros and cons of various tools, and she didn’t have a budget for anything fancy. The inspiration and creativity I felt that day derived from my in-person interaction with my reader.

Of course, it’s not always possible to set up a call with a customer, but there are plenty of other options for engaging with readers. For instance, you might consider creating a poll for your social media audience, engaging with readers in a Twitter chat, or sending a survey to your readers in an email newsletter to learn more about what they want from your brand.

Conclusion

Ultimately, it can be difficult to stay creative when your department is primarily focused on using technical SEO to achieve major goals. And, of course, you’d never want to entirely forgo SEO for the sake of creativity, since that prevents you from reaching a larger audience and ensuring your content is useful and actionable for your readers.

Nonetheless, if there’s anything I’ve learned over the past two years as a result of our new strategy, it’s that analytics and creativity can, indeed, work hand-in-hand. Ideally, with these six tips, you’ll be able to inspire some creativity in your daily process. Feel free to comment below with your own thoughts — I’d love to hear them!

Sign up for The Moz Top 10, a semimonthly mailer updating you on the top ten hottest pieces of SEO news, tips, and rad links uncovered by the Moz team. Think of it as your exclusive digest of stuff you don’t have time to hunt down but want to read!

Reblogged 1 year ago from feedproxy.google.com

Use the Blank Sheet of Paper Test to Optimize for Natural Language Processing

Posted by Evan_Hall

If you handed someone a blank sheet of paper and the only thing written on it was the page’s title, would they understand what the title meant? Would they have a clear idea of what the actual document might be about? If so, then congratulations! You just passed the Blank Sheet of Paper Test for page titles because your title was descriptive.

The Blank Sheet of Paper Test (BSoPT) is an idea Ian Lurie has talked about a lot over the years, and recently on his new website. It’s a test to see if what you’ve written has meaning to someone who has never encountered your brand or content before. In Ian’s words, “Will this text, written on a blank sheet of paper, make sense to a stranger?” The Blank Sheet of Paper Test is about clarity without context.

But what if we’re performing the BSoPT on a machine instead of a person? Does our thought experiment still apply? I think so. Machines can’t read—even sophisticated ones like Google and Bing. They can only guess at the meaning of our content, which makes the test especially relevant.

I have an alternative version of the BSoPT, but for machines: If all a machine could see is a list of words that appear in a document and how often, could it reasonably guess what the document is about?

The Blank Sheet of Paper Test for word frequency

If you handed someone a blank sheet of paper and the only thing written on it was this table of words and frequencies, could they guess what the article is about?

An article about sharpening a knife is a pretty good guess. The article I took this word frequency table from was a how-to guide for sharpening a kitchen knife.

What if the words “step” and “how” appeared in the table? Would the person reading be more confident this article is about sharpening knives, or less? Could they tell if this article is about sharpening kitchen knives or pocket knives?

If we can’t get a pretty good idea of what the article is about based on which words it uses, then it fails the BSoPT for word frequency.

Can we still use word frequency for BERT?

Earlier natural language processing (NLP) approaches employed by search engines used statistical analysis of word frequency and word co-occurrence to determine what a page is about. They ignored the order and part of speech of the words in our content, basically treating our pages like bags of words.

The tools we used to optimize for that kind of NLP compared the word frequency of our content against our competitors, and told us where the gaps in word usage were. Hypothetically, if we added those words to our content, we would rank higher, or at least help search engines understand our content better.

Those tools still exist: Market Muse, SEMRush, seobility, Ryte, and others have some sort of word frequency or TD-IDF gap analysis capability. I’ve been using a free word frequency tool called Online Text Comparator, and it works pretty well. Are they still useful now that search engines have advanced with NLP approaches like BERT? I think so, but it’s not as simple as more words = better rankings.

BERT is a lot more sophisticated than a bag-of-words approach. BERT looks at the order of words, part of speech, and any entities present in our content. It’s robust and can be trained to do many things including question answering and named entity recognition—definitely more advanced than basic word frequency.

However, BERT still needs to look at the words present on the page to function, and word frequency is a basic summary of that. Now, word location and part of speech matter more. We can’t just sprinkle the words we found in our gap analysis around the page.

Enhancing content with word frequency tools

To help make our content unambiguous to machines, we need to make it unambiguous to users. Reducing ambiguity in our writing is about choosing words that are specific to the topic we’re writing about. If our writing uses a lot of generic verbs, pronouns, and non-thematic adjectives, then not only is our content bland, it’s hard to understand.

Consider this extreme example of non-specific language:

“The trick to finding the right chef’s knife is finding a good balance of features, qualities and price. It should be made from metal strong enough to keep its edge for a decent amount of time. You should have a comfortable handle that won’t make you tired. You don’t need to spend a lot either. The home cook doesn’t need a fancy $350 Japanese knife.”

This copy isn’t great. It looks almost machine-generated. I can’t imagine a full article written like this would pass the BSoPT for word frequency.

Here’s what the word frequency table looks like with some stop words removed:

Now suppose we used a word frequency tool on a few pages that are ranking well for “how to pick a chef’s knife” and found that these parts of speech were being used fairly often:

Entities: blade, steel, fatigue, damascus steel, santoku, Shun (brand)
Verbs
: grip, chopping
Adjectives
: perfect, hard, high-carbon

Incorporating these words into our copy would yield text that’s significantly better:

“The trick to finding the perfect chef’s knife is getting the right balance of features, qualities, and price. The blade should be made from steel hard enough to keep a sharp edge after repeated use. You should have an ergonomic handle that you can grip comfortably to prevent fatigue from extending chopping. You don’t need to spend a lot, either. The home cook doesn’t need a $350 high-carbon damascus steel santoku from Shun.”

This upgraded text will be easier for machines to classify, and better for users to read. It’s also just good writing to use words relevant to your topic.

Looking toward the future of NLP

Is improving our content with the Blank Sheet of Paper Test optimizing for BERT or other NLP algorithms? No, I don’t think so. I don’t think there is a special set of words we can add to our content to magically rank higher through exploiting BERT. I see this as a way to ensure our content is understood clearly by both users and machines.

I anticipate that we’re getting pretty close to the point where the idea of optimizing for NLP will be considered absurd. Maybe in 10 years, writing for users and writing for machines will be the same thing because of how far the technology has advanced. But even then, we’ll still have to make sure our content makes sense. And the Blank Sheet of Paper Test will still be a great place to start.

Sign up for The Moz Top 10, a semimonthly mailer updating you on the top ten hottest pieces of SEO news, tips, and rad links uncovered by the Moz team. Think of it as your exclusive digest of stuff you don’t have time to hunt down but want to read!

Reblogged 1 year ago from feedproxy.google.com

The time for a website migration is now

Each migration type has its level of risk but with proper planning, it is manageable and will set your site up for future growth.

Please visit Search Engine Land for the full article.

Reblogged 1 year ago from feeds.searchengineland.com

Don’t try to reinvent the SEO wheel, says Google’s Martin Splitt

Splitt addresses the most common indexing and JavaScript mistakes, why he doesn’t take ranking questions and shares why Google’s communications aren’t always as proactive as we’d like.

Please visit Search Engine Land for the full article.

Reblogged 1 year ago from feeds.searchengineland.com

Setting TypeScript For Modern React Projects Using Webpack And Babel

Setting TypeScript For Modern React Projects Using Webpack And Babel

Setting TypeScript For Modern React Projects Using Webpack And Babel

Blessing Krofegha

2020-05-11T10:30:00+00:00
2020-05-12T09:41:51+00:00

In this era of software development, JavaScript can be used to develop almost any type of app. However, the fact that JavaScript is dynamically typed could be a concern for most large enterprise companies, because of its loose type-checking feature.

Fortunately, we don’t have to wait until the Ecma Technical Committee 39 introduces a static type system into JavaScript. We can use TypeScript instead.

JavaScript, being dynamically typed, is not aware of the data type of a variable until that variable is instantiated at runtime. Developers who write large software programs might have a tendency to reassign a variable, declared earlier, to a value of a different type, with no warning or issue whatsoever, resulting in bugs often overlooked.

In this tutorial, we will learn what TypeScript is and how to work with it in a React project. By the end, we’ll have built a project consisting of an episode-picker app for the TV show Money Heist, using TypeScript and current React-like hooks (useState, useEffect, useReducer, useContext). With this knowledge, you can go on to experiment with TypeScript in your own projects.

This article isn’t an introduction to TypeScript. Hence, we won’t go through the basic syntax of TypeScript and JavaScript. However, you don’t have to be an expert in any of these languages to follow along, because we’ll try to follow the KISS principle (keep it simple, stupid).

What Is TypeScript?

In 2019, TypeScript was ranked the seventh most-used language and the fifth fastest-growing language on GitHub. But what exactly is TypeScript?

According to the official documentation, TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. It is developed and maintained by Microsoft and the open-source community.

“Superset” in this context means that the language contains all of the features and functionality of JavaScript and then some. TypeScript is a typed scripting language.

It offers developers more control over their code base via its type annotation, classes, and interface, sparing developers from having to manually fix annoying bugs in the console.

TypeScript wasn’t created to alter JavaScript. Instead, it expands on JavaScript with valuable new features. Any program written in plain JavaScript will also run as expected in TypeScript, including cross-platform mobile apps and back ends in Node.js.

This means you can also write React apps in TypeScript, as we will do in this tutorial.

Why TypeScript?

Perhaps, you aren’t convinced of embracing the goodness of TypeScript. Let’s consider a few of its advantages.

Fewer Bugs

We cannot eliminate all bugs in our code, but we can reduce them. TypeScript checks for types at compile-time and throws errors if the variable type changes.

Being able to find these obvious yet frequent errors this early on makes it a lot easier to manage your code with types.

Refactoring Is Easier

You probably often want to refactor quite a lot of things, but because they touch so much other code and many other files, you’re wary of modifying them.

In TypeScript, such things can often be refactored with just a click of the “Rename symbol” command in your integrated development environment (IDE).

Renaming app to expApp (Large preview)

In a dynamically typed language such as JavaScript, the only way to refactor multiple files at the same time is with the traditional “search and replace” function using regular expressions (RegExp).

In a statically typed language such as TypeScript, “search and replace” isn’t needed anymore. With IDE commands such as “Find all occurrences” and “Rename symbol”, you can see all occurrences in the app of the given function, class, or property of an object interface.

TypeScript will help you find all instances of the refactored bit, rename it, and alert you with a compile error in case your code has any type mismatches after the refactoring.

TypeScript has even more advantages than what we’ve covered here.

Disadvantages Of TypeScript

TypeScript is surely not without its disadvantages, even given the promising features highlighted above.

A False Sense Of Security

TypeScript’s type-checking feature often creates a false sense of security among developers. The type checking indeed warns us when something is wrong with our code. However, static types don’t reduce overall bug density.

Therefore, the strength of your program will depend on your usage of TypeScript, because types are written by the developer and not checked at runtime.

If you’re looking to TypeScript to reduce your bugs, please consider test-driven development instead.

Complicated Typing System

The typing system, while a great tool in many regards, can sometimes be a little complicated. This downside stems from it being fully interoperable with JavaScript, which leaves even more room for complication.

However, TypeScript is still JavaScript, so understanding JavaScript is important.

When To Use TypeScript?

I would advise you to use TypeScript in the following cases:

  • If you’re looking to building an application that will be maintained over a long period, then I would strongly recommend starting with TypeScript, because it fosters self-documenting code, thus helping other developers to understand your code easily when they join your code base.
  • If you need to create a library, consider writing it in TypeScript. It will help code editors to suggest the appropriate types to developers who are using your library.

In the last few sections, we have balanced the pros and cons of TypeScript. Let’s move on to the business of the day: setting up TypeScript in a modern React project.

Getting Started

There are several ways to set up TypeScript in a React Project. In this tutorial, we’ll be covering just two.

Method 1: Create React App + TypeScript

About two years ago, the React team released Create React App 2.1, with TypeScript support. So, you might never have to do any heavy lifting to get TypeScript into your project.

Announcement of TypeScript in Create React App (Large preview)

To start a new Create React App project, you can run this…

npx create-react-app my-app --folder-name

… or this:

yarn create react-app my-app --folder-name

To add TypeScript to a Create React App project, first install it and its respective @types:

npm install --save typescript @types/node @types/react @types/react-dom @types/jest

… or:

yarn add typescript @types/node @types/react @types/react-dom @types/jest

Next, rename the files (for example, index.js to index.tsx), and restart your development server!

That was quick, wasn’t it?

Method 2: Set Up TypeScript With Webpack

Webpack is a static module bundler for JavaScript applications. It takes all of the code from your application and makes it usable in a web browser. Modules are reusable chunks of code built from your app’s JavaScript, node_modules, images, and CSS styles, which are packaged to be easily used on your website.

Create A New Project

Let’s start by creating a new directory for our project:

mkdir react-webpack
cd react-webpack

We’ll use npm to initialize our project:

npm init -y

The command above will generate a package.json file with some default values. Let’s also add some dependencies for webpack, TypeScript, and some React-specific modules.

Installing Packages

Lastly, we’d need to install the necessary packages. Open your command-line interface (CLI) and run this:

#Installing devDependencies

npm install --save-dev @types/react @types/react-dom awesome-typescript-loader css-loader html-webpack-plugin mini-css-extract-plugin source-map-loader typescript webpack webpack-cli webpack-dev-server

#installing Dependencies
npm install react react-dom

Let’s also manually add a few different files and folders under our react-webpack folder:

  1. Add webpack.config.js to add webpack-related configurations.
  2. Add tsconfig.json for all of our TypeScript configurations.
  3. Add a new directory, src.
  4. Create a new directory, components, in the src folder.
  5. Finally, add index.html, App.tsx, and index.tsx in the components folder.

Project Structure

Thus, our folder structure will look something like this:

├── package.json
├── package-lock.json
├── tsconfig.json
├── webpack.config.js
├── .gitignore
└── src
    └──components
        ├── App.tsx
        ├── index.tsx
        ├── index.html

Start Adding Some Code

We’ll start with index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>React-Webpack Setup</title>
</head>
<body>
  <div id="output"></div>
</body>
</html>

This will create the HTML, with an empty div with an ID of output.

Let’s add the code to our React component App.tsx:

import * as React from "react";
export interface HelloWorldProps {
  userName: string;
  lang: string;
}
export const App = (props: HelloWorldProps) => (
  <h1>
    Hi {props.userName} from React! Welcome to {props.lang}!
  </h1>
);

We’ve created an interface object and named it HelloWorldProps, with userName and lang having a string type.

We passed props to our App component and exported it.

Now, let’s update the code in index.tsx:

import * as React from "react";
import * as ReactDOM from "react-dom";
import { App } from "./App";
ReactDOM.render(
  <App userName="Beveloper" lang="TypeScript" />,
  document.getElementById("output")
);

We just imported the App component into index.tsx. When webpack sees any file with the extension .ts or .tsx, it will transpile that file using the awesome-typescript-loader library.

TypeScript Configuration

We’ll then add some configuration to tsconfig.json:

{
  "compilerOptions": {
    "jsx": "react",
    "module": "commonjs",
    "noImplicitAny": true,
    "outDir": "./build/",
    "preserveConstEnums": true,
    "removeComments": true,
    "sourceMap": true,
    "target": "es5"
  },
  "include": [
    "src/components/index.tsx"
  ]
}

Let’s also look at the different options we added to tsconfig.json:

  • compilerOptions
    Represents the different compiler options.
  • jsx:react
    Adds support for JSX in .tsx files.
  • lib
    Adds a list of library files to the compilation (for example, using es2015 allows us to use ECMAScript 6 syntax).
  • module
    Generates module code.
  • noImplicitAny
    Raises errors for declarations with an implied any type.
  • outDir
    Represents the output directory.
  • sourceMap
    Generates a .map file, which can be very useful for debugging the app.
  • target
    Represents the target ECMAScript version to transpile our code down to (we can add a version based on our specific browser requirements).
  • include
    Used to specify the file list to be included.

Webpack Configuration

Let’s add some webpack configuration to webpack.config.js.

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
  entry: "./src/components/index.tsx",
  target: "web",
  mode: "development",
  output: {
    path: path.resolve(\__dirname, "build"),
    filename: "bundle.js",
  },
  resolve: {
    extensions: [".js", ".jsx", ".json", ".ts", ".tsx"],
  },
  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/,
        loader: "awesome-typescript-loader",
      },
      {
        enforce: "pre",
        test: /\.js$/,
        loader: "source-map-loader",
      },
      {
        test: /\.css$/,
        loader: "css-loader",
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(\__dirname, "src", "components", "index.html"),
    }),
    new MiniCssExtractPlugin({
      filename: "./src/yourfile.css",
    }),
  ],
};

Let’s look at the different options we’ve added to webpack.config.js:

  • entry
    This specifies the entry point for our app. It may be a single file or an array of files that we want to include in our build.
  • output
    This contains the output configuration. The app looks at this when trying to output bundled code from our project to the disk. The path represents the output directory for code to be outputted to, and the file name represents the file name for the same. It is generally named bundle.js.
  • resolve
    Webpack looks at this attribute to decide whether to bundle or skip the file. Thus, in our project, webpack will consider files with the extensions .js, .jsx, .json, .ts , and .tsx for bundling.
  • module
    We can enable webpack to load a particular file when requested by the app, using loaders. It takes a rules object that specifies that:

    • any file that ends with the extension .tsx or .ts should use awesome-typescript-loader to be loaded;
    • files that end with the .js extension should be loaded with source-map-loader;
    • files that end with the .css extension should be loaded with css-loader.
  • plugins
    Webpack has its own limitations, and it provides plugins to overcome them and extend its capabilities. For example, html-webpack-plugin creates a template file that is rendered to the browser from the index.html file in the ./src/component/index.html directory.

MiniCssExtractPlugin renders the parent CSS file of the app.

Adding Scripts To package.json

We can add different scripts to build React apps in our package.json file:

"scripts": {
"start": "webpack-dev-server --open",
"build": "webpack"
},

Now, run npm start in your CLI. If it all went well, you should see this:

React-Webpack setup output (Large preview)

If you have a knack for webpack, clone the repository for this setup, and use it across your projects.

Creating Files

Create a src folder and an index.tsx file. This will be the base file that renders React.

Now, if we run npm start, it will run our server and open a new tab. Running npm run build will build webpack for production and will create a build folder for us.

We have seen how to set up TypeScript from scratch using the Create React App and webpack configuration method.

One of the quickest ways to get a full grasp of TypeScript is by converting one of your existing vanilla React projects to TypeScript. Unfortunately, incrementally adopting TypeScript in an existing vanilla React project is stressful because it entails having to eject or rename all of the files, which would result in conflicts and a giant pull request if the project belonged to a large team.

Next, we’ll look at how to easily migrate a React project to TypeScript.

Migrate An Existing Create React App To TypeScript

To make this process more manageable, we’ll break it down into steps, which will enable us to migrate in individual chunks. Here are the steps we’ll take to migrate our project:

  1. Add TypeScript and types.
  2. Add tsconfig.json.
  3. Start small.
  4. Rename files extension to .tsx.
1. Add TypeScript to the Project

First, we’ll need to add TypeScript to our project. Assuming that your React project was bootstrapped with Create React App, we can run the following:

# Using npm
npm install --save typescript @types/node @types/react @types/react-dom @types/jest

# Using Yarn
yarn add typescript @types/node @types/react @types/react-dom @types/jest

Notice that we haven’t changed anything to TypeScript yet. If we run the command to start the project locally (npm start or yarn start), nothing changes. If that’s the case, then great! We’re ready for the next step.

2. Add the tsconfig.json File

Before taking advantage of TypeScript, we need to configure it via the tsconfig.json file. The simplest way to get started is to scaffold one using this command:

npx tsc --init

This gets us some basics, with a lot of commented code. Now, replace all of the code in tsconfig.json with this:

{
    "compilerOptions": {
      "jsx": "react",
      "module": "commonjs",
      "noImplicitAny": true,
      "outDir": "./build/",
      "preserveConstEnums": true,
      "removeComments": true,
      "sourceMap": true,
      "target": "es5"
    },
    "include": [
      "./src/**/**/\*"
    ]
  }

TypeScript Configuration

Let’s also look at the different options we added to tsconfig.json:

  • compilerOptions
    Represents the different compiler options.

    • target
      Translates newer JavaScript constructs down to an older version, like ECMAScript 5.
    • lib
      Adds a list of library files to the compilation (for example, using es2015 allows us to use ECMAScript 6 syntax).
    • jsx:react
      Adds support for JSX in .tsx files.
    • lib
      Adds a list of library files to the compilation (for example, using es2015 allows us to use ECMAScript 6 syntax).
    • module
      Generates module code.
    • noImplicitAny
      Used to raise errors for declarations with an implied any type.
    • outDir
      Represents the output directory.
    • sourceMap
      Generates a .map file, which can be very useful for debugging our app.
    • include
      Used to specify the file list to be included.

Configurations options will vary, according to a project’s demand. You might need to check the TypeScript options spreadsheet to figure out what would fit your project.

We have only taken the required actions to get things ready. Our next step is to migrate a file to TypeScript.

3. Start With a Simple Component

Take advantage of TypeScript’s ability to be gradually adopted. Go one file at a time at your own pace. Do what makes sense for you and your team. Don’t try to tackle it all at once.

To properly convert this, we need to do two things:

  1. Change the file extension to .tsx.
  2. Add the type annotation (which would require some TypeScript knowledge).
4.Rename File Extensions to .tsx

In a large code base, it might seem tiring to rename files individually.

Rename multiples files on macOS

Renaming multiple files can be a time-waster. Here is how you can do it on a Mac. Right-click (or Ctrl + click, or click with two fingers simultaneously on the trackpad if you are using a MacBook) on the folder that contains the files you want to rename. Then, click “Reveal in Finder”. In the Finder, select all of the files you want to rename. Right-click the selected files, and choose “Rename X items…” Then, you will see something like this:

Rename files on a Mac (Large preview)

Insert the string you want to find, and the string with which you want to replace that found string, and hit “Rename”. Done.

Rename multiples files on Windows

Renaming multiple files on Windows is beyond the scope of this tutorial, but a complete guide is available.
You would usually get errors after renaming the files; you just need to add the type annotations. You can brush up on this in the documentation.

We have covered how to set up TypeScript in a React app. Now, let’s build an episode-picker app for Money Heist using TypeScript.

We won’t cover the basic types of TypeScript. Going through the documentation before continuing in this tutorial is required.

Time to Build

To make this process feel less daunting, we’ll break this down into steps, which will enable us to build the app in individual chunks. Here are all of the steps we’ll take to build the Money Heist episode-picker:

  • Scaffold a Create React App.
  • Fetch episodes.
    • Create the appropriate types and interfaces for our episodes in interface.ts.
    • Set up store for fetching episodes in store.tsx.
    • Create the action for fetching episodes in action.ts.
    • Create an EpisodeList.tsx component that holds the episodes fetched.
    • Import the EpisodesList component to our home page using React Lazy and Suspense.
  • Add episodes.
    • Set up store to add episodes in store.tsx.
    • Create the action for adding episodes in action.ts.
  • Remove episodes.
    • Set up store for deleting episodes in store.tsx.
    • Create the action for deleting episodes in action.ts.
  • Favorite episode.
    • Import EpisodesList component in favorite episode.
    • Render EpisodesList inside favorite episode.
  • Using Reach Router for navigation.

Set Up React

The easiest way to set up React is to use Create React App. Create React App is an officially supported way to create single-page React applications. It offers a modern build setup with no configuration.

We’ll make use of it to bootstrap the application we’ll be building. From your CLI, run the command below:

npx create-react-app react-ts-app && cd react-ts-app

Once the installation is successful, start the React server by running npm start.

React starter page (Large preview)

Understanding Interfaces And Types In Typescript

Interfaces in TypeScript are used when we need to give types to objects properties. Hence, we would be using interfaces to define our types.

interface Employee {
  name: string,
  role: string
  salary: number
}
const bestEmployee: Employee= {
  name: 'John Doe',
  role: 'IOS Developer',
  salary: '$8500'    //notice we are using a string
}

When compiling the code above, we would see this error: “Types of property salary are incompatible. Type string is not assignable to type number.”

Such errors happen in TypeScript when a property or variable is assigned a type other than the defined type. Specifically, the snippet above means that the salary property was assigned a string type instead of a number type.

Let’s create an interface.ts file in our src folder. Copy and paste this code into it:

/**
|--------------------------------------------------
| All the interfaces!
|--------------------------------------------------
*/
export interface IEpisode {
  airdate: string
  airstamp: string
  airtime: string
  id: number
  image: { medium: string; original: string }
  name: string
  number: number
  runtime: number
  season: number
  summary: string
  url: string
}
export interface IState {
  episodes: Array<IEpisode>
  favourites: Array<IEpisode>
}
export interface IAction {
  type: string
  payload: Array<IEpisode> | any
}
export type Dispatch = React.Dispatch<IAction>
export type FavAction = (
  state: IState,
  dispatch: Dispatch,
  episode: IEpisode
) => IAction

export interface IEpisodeProps {
  episodes: Array<IEpisode>
  store: { state: IState; dispatch: Dispatch }
  toggleFavAction: FavAction
  favourites: Array<IEpisode>
}
export interface IProps {
  episodes: Array<IEpisode>
  store: { state: IState; dispatch: Dispatch }
  toggleFavAction: FavAction
  favourites: Array<IEpisode>
}

It’s a good practice to add an “I” to the name of the interface. It makes the code readable. However, you may decide to exclude it.

IEpisode Interface

Our API returns a set of properties such as airdate, airstamp, airtime, id, image, name, number, runtime, season, summary, and url. Hence, we defined an IEpisode interface and set the appropriate data types to the object properties.

IState Interface

Our IState interface has episodes and favorites properties, respectively, and an Array<IEpisode> interface.

IAction

The IAction interface properties are payload and type. The type property has a string type, while the payload has a type of Array | any.

Note that Array | any means an array of the episode interface or any type.

The Dispatch type is set to React.Dispatch and a <IAction> interface. Note that React.Dispatch is the standard type for the dispatch function, according to the @types/react code base, while <IAction> is an array of the Interface action.

Also, Visual Studio Code has a TypeScript checker. So, by merely highlighting or hovering over code, it’s smart enough to suggest the appropriate type.

In other words, for us to make use of our interface across our apps, we need to export it. So far, we have our store and our interfaces that hold the type of our object. Let’s now create our store.
Note that the other interfaces follow the same conventions as the ones explained.

Fetch Episodes

Creating a Store

To fetch our episodes, we need a store that holds the initial state of the data and that defines our reducer function.

We’ll make use of useReducer hook to set that up. Create a store.tsx file in your src folder. Copy and paste the following code into it.

import React, { useReducer, createContext } from 'react'
import { IState, IAction } from './types/interfaces'
const initialState: IState = {
  episodes: [],
  favourites: []
}
export const Store = createContext(initialState)
const reducer = (state: IState, action: IAction): IState => {
  switch (action.type) {
    case 'FETCH_DATA':
      return { ...state, episodes: action.payload }
    default:
      return state
  }
}
export const StoreProvider = ({ children }: JSX.ElementChildrenAttribute): JSX.Element => {
  const [state, dispatch] = useReducer(reducer, initialState)
  return {children}
}

The following are the steps we’ve taken to create the store:

  • In defining our store, we need the useReducer hook and the createContext API from React, which is why we imported it.
  • We imported IState and IAction from ./types/interfaces.
  • We declared an initialState object with a type of IState, and properties of episodes and favorites, which are both set to an empty array, respectively.
  • Next, we created a Store variable that holds the createContext method and that is passed the initialState.

The createContext method type is <IState | any>, which means it could be a type of <IState> or any. We will see the any type used often in this article.

  • Next, we declared a reducer function and passed in state and action as parameters. The reducer function has a switch statement that checks the value of action.type. If the value is FETCH_DATA, then it returns an object that has a copy of our state (...state) and of the episode state that holds our action payload.
  • In the switch statement, we return a state of default.

Note that the state and action parameters in the reducer function have IState and IAction types, respectively. Also, the reducer function has a type of IState.

  • Lastly, we declared a StoreProvider function. This will give all components in our app access to the store.
  • This function takes children as a prop, and inside the StorePrivder function, we declared the useReducer hook.
  • We destructured state and dispatch.
  • In order to make our store accessible to all components, we passed in an object value containing state and dispatch.

The state that contains our episodes and favorites state will be made accessible by other components, while the dispatch is a function that changes the state.

  • We will export Store and StoreProvider, so that it can be used across our application.

Create Action.ts

We’ll need to make requests to the API to fetch the episodes that will be shown the user. This will be done in an action file. Create an Action.ts file, and then paste the following code:

import { Dispatch } from './interface/interfaces'
export const fetchDataAction = async (dispatch: Dispatch) => {
  const URL =
    'https://api.tvmaze.com/singlesearch/shows?q=la-casa-de-papel&embed=episodes'

  const data = await fetch(URL)
  const dataJSON = await data.json()
  return dispatch({
    type: 'FETCH_DATA',
    payload: dataJSON.\_embedded.episodes
  })
}

First, we need to import our interfaces so that they can be used in this file. The following steps were taken to create the action:

  • The fetchDataAction function takes dispatch props as a parameter.
  • Because our function is asynchronous, we would be using async and await.
  • We create a variable(URL) that holds our API endpoint.
  • We have another variable named data that holds the response from the API.
  • Then, we store the JSON response in dataJSON, after we have gotten the response in JSON format by calling data.json().
  • Lastly, we return a dispatch function that has a property of type and a string of FETCH_DATA. It also has a payload(). _embedded.episodes is the array of the episodes object from our endpoint.

Note that the fetchDataAction function fetches our endpoint, converts it to JSON objects, and returns the dispatch function, which updates the state declared earlier in the Store.

The exported dispatch type is set to React.Dispatch. Note that React.Dispatch is the standard type for the dispatch function according to the @types/react code base, while <IAction> is an array of the Interface Action.

EpisodesList Component

In order to maintain the reusability of our app, we will keep all fetched episodes in a separate file, and then import the file in our homePage component.

In the components folder, create an EpisodesList.tsx file, and copy and paste the following code to it:

import React from 'react'
import { IEpisode, IProps } from '../types/interfaces'
const EpisodesList = (props: IProps): Array<JSX.Element> => {
  const { episodes } = props
  return episodes.map((episode: IEpisode) => {
    return (
      <section key={episode.id} className='episode-box'>
        <img src={!!episode.image ? episode.image.medium : ''} alt={`Money Heist ${episode.name}`} />
        <div>{episode.name}</div>
        <section style={{ display: 'flex', justifyContent: 'space-between' }}>
          <div>
            Season: {episode.season} Number: {episode.number}
          </div>
          <button
            type='button'
          >
            Fav
          </button>
        </section>
      </section>
    )
  })
}
export default EpisodesList
  • We import IEpisode and IProps from interfaces.tsx.
  • Next, we create an EpisodesList function that takes props. The props will have a type of IProps, while the function has a type of Array<JSX.Element>.

Visual Studio Code suggests that our function type be written as JSX.Element[].

Visual Studio Code suggests a type (Large preview)

While Array<JSX.Element> is equal to JSX.Element[], Array<JSX.Element> is called the generic identity. Hence, the generic pattern will be used often in this article.

  • Inside the function, we destructure the episodes from props, which has the IEpisode as a type.

Read about the generic identity, This knowledge will be needed as we proceed.

  • We returned the episodes props and mapped through it to return a few HTML tags.
  • The first section holds the key, which is episode.id, and a className of episode-box, which will be created later. We know that our episodes have images; hence, the image tag.
  • The image has a ternary operator that checks if there’s either an episode.image or an episode.image.medium. Else, we display an empty string if no image is found. Also, we included the episode.name in a div.

In section, we show the season that an episode belongs to and its number. We have a button with the text Fav. We’e exported the EpisodesList component so that we can use it across our app.

Home Page Component

We want the home page to trigger the API call and display the episodes using the EpisodesList component we created. Inside the components folder, create the HomePage component, and copy and paste the following code to it:

import React, { useContext, useEffect, lazy, Suspense } from 'react'
import App from '../App'
import { Store } from '../Store'
import { IEpisodeProps } from '../types/interfaces'
import { fetchDataAction } from '../Actions'
const EpisodesList = lazy<any>(() => import('./EpisodesList'))
  const HomePage = (): JSX.Element => {
  const { state, dispatch } = useContext(Store)
  useEffect(() => {
    state.episodes.length === 0 && fetchDataAction(dispatch)
  })
  const props: IEpisodeProps = {
    episodes: state.episodes,
    store: { state, dispatch }
  }
  return (
    <App>
      <Suspense fallback={<div>loading...</div>}>
        <section className='episode-layout'>
          <EpisodesList {...props} />
        </section>
      </Suspense>
    </App>
  )
  }
export default HomePage
  • We import useContext, useEffect, lazy, and Suspense from React. The imported app component is the bedrock upon which all other components must receive the value of the store.
  • We also import Store, IEpisodeProps, and FetchDataAction from their respective files.
  • We import the EpisodesList component using the React.lazy feature available in React 16.6.

React lazy loading supports the code-splitting convention. Thus, our EpisodesList component is loaded dynamically, instead of being loaded at once, thereby improving the performance of our app.

  • We destructure the state and dispatch as props from the Store.
  • The ampersand (&&) in the useEffect hook checks if our episodes state is empty (or equal to 0). Else, we return the fetchDataAction function.
  • Lastly, we return the App component. Inside it, we use the Suspense wrapper, and set fallback to a div with the loading text. This will be displayed to the user while we await the response from the API.
  • The EpisodesList component will mount when the data is available, and the data that will contain the episodes is what we spread into it.

Set Up Index.txs

The Homepage component needs to be a child of the StoreProvider. We’ll have to do that in the index file. Rename index.js to index.tsx and paste the following code:

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import { StoreProvider } from './Store'
import HomePage from './components/HomePage'
ReactDOM.render(
  <StoreProvider>
      <HomePage />
  </StoreProvider>,
  document.getElementById('root')
)

We import StoreProvider, HomePage, and index.css from their respective files.
We wrap the HomePage component in our StoreProvider. This makes it possible for the Homepage component to access the store, as we saw in the previous section.

We have come a long way. Let’s check what the app looks like, without any CSS.

App without CSS (Large preview)

Create Index.css

Delete the code in the index.css file and replace it with this:

html {
  font-size: 14px;
}
body {
  margin: 0;
  padding: 0;
  font-size: 10px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
    "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
.episode-layout {
  display: flex;
  flex-wrap: wrap;
  min-width: 100vh;
}
.episode-box {
  padding: .5rem;
}
.header {
  display: flex;
  justify-content: space-between;
  background: white;
  border-bottom: 1px solid black;
  padding: .5rem;
  position: sticky;
  top: 0;
}

Our app now has a look and feel. Here’s how it looks with CSS.

(Large preview)

Now we see that our episodes can finally be fetched and displayed, because we’ve adopted TypeScript all the way. Great, isn’t it?

Add Favorite Episodes Feature

Let’s add functionality that adds favorite episodes and that links it to a separate page. Let’s go back to our Store component and add a few lines of code:

Note that the highlighted code is newly added:

import React, { useReducer, createContext } from 'react'
import { IState, IAction } from './types/interfaces'
const initialState: IState = {
  episodes: [],
  favourites: []
}
export const Store = createContext<IState | any>(initialState)
const reducer = (state: IState, action: IAction): IState => {
  switch (action.type) {
    case 'FETCH_DATA':
      return { ...state, episodes: action.payload }
    case 'ADD_FAV':
      return { ...state, favourites: [...state.favourites, action.payload] }
    default:
      return state
  }
}
export const StoreProvider = ({ children }: JSX.ElementChildrenAttribute): JSX.Element => {
  const [state, dispatch] = useReducer(reducer, initialState)
  return <Store.Provider value={{ state, dispatch }}>{children}</Store.Provider>
}

To implement the “Add favorite” feature to our app, the ADD_FAV case is added. It returns an object that holds a copy of our previous state, as well as an array with a copy of the favorite state, with the payload.

We need an action that will be called each time a user clicks on the FAV button. Let’s add the highlighted code to index.tx:

import { IAction, IEpisode, Dispatch } from './types/interfaces'
export const fetchDataAction = async (dispatch: Dispatch) => {
  const URL =
    'https://api.tvmaze.com/singlesearch/shows?q=la-casa-de-papel&embed=episodes'

  const data = await fetch(URL)
  const dataJSON = await data.json()
  return dispatch({
    type: 'FETCH_DATA',
    payload: dataJSON._embedded.episodes
  })
}
export const toggleFavAction = (dispatch: any, episode: IEpisode | any): IAction => {
  let dispatchObj = {
    type: 'ADD_FAV',
    payload: episode
  }
  return dispatch(dispatchObj)
}

We create a toggleFavAction function that takes dispatch and episodes as parameters, and any and IEpisode|any as their respective types, with IAction as our function type. We have an object whose type is ADD_FAV and that has episode as its payload. Lastly, we just return and dispatch the object.

We will add some more snippets to EpisodeList.tsx. Copy and paste the highlighted code:

import React from 'react'
import { IEpisode, IProps } from '../types/interfaces'
const EpisodesList = (props: IProps): Array<JSX.Element> => {
  const { episodes, toggleFavAction, favourites, store } = props
  const { state, dispatch } = store

  return episodes.map((episode: IEpisode) => {
    return (
      <section key={episode.id} className='episode-box'>
        <img src={!!episode.image ? episode.image.medium : ''} alt={`Money Heist - ${episode.name}`} />
        <div>{episode.name}</div>
        <section style={{ display: 'flex', justifyContent: 'space-between' }}>
          <div>
            Seasion: {episode.season} Number: {episode.number}
          </div>
          <button
            type='button'
            onClick={() => toggleFavAction(state, dispatch, episode)}
          >
            {favourites.find((fav: IEpisode) => fav.id === episode.id)
              ? 'Unfav'
              : 'Fav'}
          </button>
        </section>
      </section>
    )
  })
}
export default EpisodesList

We include togglefavaction, favorites, and store as props, and we destructure state, a dispatch from the store. In order to select our favorite episode, we include the toggleFavAction method in an onClick event, and pass the state, dispatch and episode props as arguments to the function.

Lastly, we loop through the favorite state to check if fav.id(favorite ID) matches the episode.id. If it does, we toggle between the Unfav and Fav text. This helps the user know if they have favorited that episode or not.

We are getting close to the end. But we still need a page where favorite episodes can be linked to when the user chooses among the episodes on the home page.

If you’ve gotten this far, give yourself a pat on the back.

Favpage Component

In the components folder, create a FavPage.tsx file. Copy and paste the following code to it:

import React, { lazy, Suspense } from 'react'
import App from '../App'
import { Store } from '../Store'
import { IEpisodeProps } from '../types/interfaces'
import { toggleFavAction } from '../Actions'
const EpisodesList = lazy<any>(() => import('./EpisodesList'))
export default function FavPage(): JSX.Element {
  const { state, dispatch } = React.useContext(Store)
  const props: IEpisodeProps = {
    episodes: state.favourites,
    store: { state, dispatch },
    toggleFavAction,
    favourites: state.favourites
  }
  return (
    <App>
      <Suspense fallback={<div>loading...</div>}>
        <div className='episode-layout'>
          <EpisodesList {...props} />
        </div>
      </Suspense>
    </App>
  )
}

To create the logic behind choosing favorite episodes, we’ve written a little code. We import lazy and Suspense from React. We also import Store, IEpisodeProps, and toggleFavAction from their respective files.

We import our EpisodesList component using the React.lazy feature. Lastly, we return the App component. Inside it, we use the Suspense wrapper, and set a fallback to a div with the loading text.

This works similar to the Homepage component. This component will access the store to obtain the episodes the user has favorited. Then, the list of episodes gets passed to the EpisodesList component.

Let’s add a few more snippets to the HomePage.tsx file.

Include the toggleFavAction from ../Actions. Also include the toggleFavAction method as props.

import React, { useContext, useEffect, lazy, Suspense } from 'react'
import App from '../App'
import { Store } from '../Store'
import { IEpisodeProps } from '../types/interfaces'
import { fetchDataAction, toggleFavAction } from '../Actions'
const EpisodesList = lazy<any>(() => import('./EpisodesList'))
  const HomePage = (): JSX.Element => {
  const { state, dispatch } = useContext(Store)
  useEffect(() => {
    state.episodes.length === 0 && fetchDataAction(dispatch)
  })
  const props: IEpisodeProps = {
    episodes: state.episodes,
    store: { state, dispatch },
    toggleFavAction,
    favourites: state.favourites
  }
  return (
    <App>
      <Suspense fallback={<div>loading...</div>}>
        <section className='episode-layout'>
          <EpisodesList {...props} />
        </section>
      </Suspense>
    </App>
  )
  }
export default HomePage

Our FavPage needs to be linked, so we need a link in our header in App.tsx. To achieve this, we use Reach Router, a library similar to React Router. William Le explains the differences between Reach Router and React Router.

In your CLI, run npm install @reach/router @types/reach__router. We are installing both the Reach Router library and reach-router types.

Upon successful installation, import Link from @reach/router.

import React, { useContext, Fragment } from 'react'
import { Store } from './tsx'
import { Link } from '@reach/router'
 const App = ({
  children
}: {
  children: JSX.Element
   }): JSX.Element => {
   const { state } = useContext(Store)

  return (
    <Fragment>
      <header className='header'>
        <div>
          <h1>Money Heist</h1>
          <p>Pick your favourite episode</p>
        </div>
        <div>
          <Link to='/'>Home</Link>
          <Link to='/faves'>Favourite(s): {state.favourites.length}</Link>
        </div>
      </header>
      {children}
    </Fragment>
  )
 }
export default App

We destructure the store from useContext. Lastly, our home will have a Link and a path to /, while our favorite has a path to /faves.

{state.favourites.length} checks for the number of episodes in the favorites states and displays it.

Finally, in our index.tsx file, we import the FavPage and HomePage components, respectively, and wrap them in the Router.

Copy the highlighted code to the existing code:

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import { StoreProvider } from './Store'
import { Router, RouteComponentProps } from '@reach/router'
import HomePage from './components/HomePage'
import FavPage from './components/FavPage'
const RouterPage = (
  props: { pageComponent: JSX.Element } & RouteComponentProps
) => props.pageComponent
ReactDOM.render(
  <StoreProvider>
   <Router>
      <RouterPage pageComponent={<HomePage />} path='/' />
      <RouterPage pageComponent={<FavPage />} path='/faves' />
    </Router>
  </StoreProvider>,
  document.getElementById('root')
)

Now, let’s see how the implemented ADD_FAV works.

The “Add favorite” code works (Large preview)

Remove Favorite Functionality

Finally, we will add the “Remove episode feature”, so that when the button is clicked, we toggle between adding or removing a favorite episode. We will display the number of episodes added or removed in the header.

STORE

To create the “Remove favorite episode” functionality, we will add another case in our store. So, go over to Store.tsx and add the highlighted code:

import React, { useReducer, createContext } from 'react'
import { IState, IAction } from './types/interfaces'
const initialState: IState = {
  episodes: [],
  favourites: []
}
export const Store = createContext<IState | any>(initialState)
const reducer = (state: IState, action: IAction): IState => {
  switch (action.type) {
    case 'FETCH_DATA':
      return { ...state, episodes: action.payload }
    case 'ADD_FAV':
      return { ...state, favourites: [...state.favourites, action.payload] }
    case 'REMOVE_FAV':
      return { ...state, favourites: action.payload }
    default:
      return state
  }
}
export const StoreProvider = ({ children }: JSX.ElementChildrenAttribute): JSX.Element => {
  const [state, dispatch] = useReducer(reducer, initialState)
  return {children}
}

We add yet another case named REMOVE_FAV and return an object containing the copy of our initialState. Also, the favorites state contains the action payload.

ACTION

Copy the following highlighted code and paste it in action.ts:

import { IAction, IEpisode, IState, Dispatch } from './types/interfaces'
export const fetchDataAction = async (dispatch: Dispatch) => {
  const URL =
    'https://api.tvmaze.com/singlesearch/shows?q=la-casa-de-papel&embed=episodes'

  const data = await fetch(URL)
  const dataJSON = await data.json()
  return dispatch({
    type: 'FETCH_DATA',
    payload: dataJSON.\_embedded.episodes
  })
}
//Add IState withits type
export const toggleFavAction = (state: IState, dispatch: any, episode: IEpisode | any): IAction => {
  const episodeInFav = state.favourites.includes(episode)
  let dispatchObj = {
    type: 'ADD_FAV',
    payload: episode
  }
  if (episodeInFav) {
    const favWithoutEpisode = state.favourites.filter(
      (fav: IEpisode) => fav.id !== episode.id
    )
    dispatchObj = {
      type: 'REMOVE_FAV',
      payload: favWithoutEpisode
    }
  }
  return dispatch(dispatchObj)
}

We import the IState interface from ./types/interfaces, because we’ll need to pass it as the type to the state props in the toggleFavAction function.

An episodeInFav variable is created to check if there’s an episode that exists in the favorites state.

We filter through the favorites state to check if a favorite ID doesn’t equal an episode ID. Thus, the dispatchObj is reassigned a type of REMOVE_FAV and a payload of favWithoutEpisode.

Let’s preview the result of our app.

Conclusion

In this article, we’ve seen how to set up TypeScript in a React project, and how to migrate a project from vanilla React to TypeScript.

We’ve also built an app with TypeScript and React to see how TypeScript is used in React projects.
I trust you were able to learn a few things.

Please do share your feedback and experiences with TypeScript in the comments section below. I’d love to see what you come up with!

The supporting repository for this article is available on GitHub.

References

  1. How To Migrate A React App To TypeScript,” Joe Previte
  2. Why And How To Use TypeScript In Your React App?,” Mahesh Haldar
Smashing Editorial
(ks, ra, il, al)
Reblogged 1 year ago from www.smashingmagazine.com

The return of retail stores: If you reopen will they come?

Surveys say consumers will be cautious, but foot traffic from Georgia suggests the novelty of offline shopping may lure some people back into stores.

Please visit Marketing Land for the full article.

Reblogged 1 year ago from feeds.marketingland.com

Many marketers unlikely to attend in-person conferences until COVID-19 vaccine

Overall, respondents said there was just a 4-out-of-10 chance they would attend a conference through the rest of the year.

Please visit Search Engine Land for the full article.

Reblogged 1 year ago from feeds.searchengineland.com