Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
December 19, 2021 09:30 pm GMT

Creating React Widgets that can be embedded on any website, by anyone

Why would I do this?
One example is for versatility in a widget youre making. Either for a client or for the world. A widget should be embeddable in as many places as possible, regardlesss of the software. Whether that website is made using WebFlow, WordPress, Shopify, Drupal, doesnt matter. Additionally, its common for a widget to exist multiple times on the same page. Lets imagine a widget where we display the last 5 posts of a given subreddit. I should be able to embed that widget multiple times, for multiple subreddits, on the same page.

Keep in mind, we arent building this widget for React developers. If that were the case, wed just build a React Component and publish it on npm. Instead, were building a widget that can be used by anyone, even a non-coder, outside of React.

Well go over exactly how to do this. Well start off by teaching you how to initialize multiple versions of your React App on the same page. Then, well learn how to pass data down the DOM, into our React App. This will allow us to present each of those widgets in different ways, by setting some attributes. Attributes which your customers can easily configure, without knowing how to code.

To get started, lets initialize a typical react app, using create-react-app.

npx create-react-app reddit-widget

ReactDOMs Render Function
When you first initialize a React App using create-react-app, youll notice React attaches itself to a single element.

ReactDOM.render(  <React.StrictMode>    <App />  </React.StrictMode>,  document.getElementById('root'));

ReactDOMs render function primarily takes two arguments. The first is the React Component youll be injecting into the DOM. The second is the actual DOM element youll be injecting the React Component into.

In the case above, were injecting our component (wrapped in Reacts Strict Mode), into the #root div container in the DOM. Which you can find by navigating to public/index.html.

<div id="root"></div>

Multiple Instanes of React
Now, what happens if we want multiple instances of this React App? We know how ReactDOMs render function works. Instead of injecting our app into a single div in the DOM, lets inject it into multiple.

First, well update index.js to iterate over multiple divs. To do this, well use document.querySelectorAll and search for all divs with a reddit_widget class specified. Then, well inject our React App into each of them.

// Find all widget divsconst WidgetDivs = document.querySelectorAll('.reddit_widget')// Inject our React App into eachWidgetDivs.forEach(Div => {  ReactDOM.render(    <React.StrictMode>      <App />    </React.StrictMode>,    Div  );})

At this point, our React App will be blank. Thats because we dont have any divs with the reddit_widget class yet. Lets update our public/index.html file.

   <div class="reddit_widget"></div>    <div class="reddit_widget"></div>

Great, now we have multiple versions of our React App running at the same time!

Passing Data Attributes
So we have our React App rendering multiple times in a page. This within itself isnt useful. We want each instance of our app to contain different data or functionality.

There are tons of ways to pass data to and from a React App. In this article, well cover using data attributes.
Reading DOM attributes in a React component
In React, we use Props to attach useful data to our components. In HTML, we have data attributes. Which, together with a bit of JavaScript, can be just as powerful.

First, lets attach some data attributes to our DOM elements in public/index.html.

<div class="reddit_widget" data-subreddit="javascript"></div><div class="reddit_widget" data-subreddit="reactjs"></div>

Now, lets read those data attributes in our React App. There are a number of ways we can do this.

  1. We can use Div.getAttribute("data-subreddit") to get our attribute from each DOM element. We can pass this a subreddit prop to our React component.

2.Similar to option 1, but using the dataset property (IE: Div.dataset.subreddit).

  1. We can pass the entire DOM element as a prop, to our React component. Allowing us to access the entire DOM element for each App. From there, we can do anything with the dom element. Including getting the attributes.For more information, check out using data attributes.

For this article, Well go with option 3.

// index.js WidgetDivs.forEach(Div => {  ReactDOM.render(    <React.StrictMode>      <App domElement={Div} />    </React.StrictMode>,    Div  );})
// src/App.js function App({ domElement }) {  const subreddit = domElement.getAttribute("data-subreddit")  return (    <div className="App">      <header className="App-header">        <img src={logo} className="App-logo" alt="logo" />        <p>          My favorite subreddit is /r/{subreddit}        </p>        <a          className="App-link"          href="https://reactjs.org"          target="_blank"          rel="noopener noreferrer"        >          Learn React        </a>      </header>    </div>  );}

Great! Now we are successfully passing data from the DOM to our React App. This opens the door to tons of possibilities. We can create entirely different versions of our app, based on the attributes passed from the DOM

Example of a "real world" reddit widget
For the sake of this article, Ill assume youre already familiar with a few basic React concepts. IE: Data Fetching as well as Components and Props. So I wont dive into the changes made to pull data from Reddits API & display the lists. If youd like a separate article on this, please comment below. However, I feel this is already covered extensively.

To make this widget even more useful and "complete", well fetch some data from Reddits API. We want to include some of the latest posts, along with links to them. We also want to include a link to the subreddit itself. Finally, its common practice for widgets to include a "powered by" notice. Especially in a "freemium" pricing model. This allows other people to discover your widget and also become customers. Maybe even paying customers.

Heres an example of what that looks like.

import React, { useEffect, useState } from 'react';import './App.css';// Render each postfunction renderPost(post){  const { data: { title, url, author, id } } = post  const authorUrl = `https://www.reddit.com/u/${author}`  return (    <div className="reddit_widget__post" key={id}>      <div className="reddit_widget__posted_by">        posted by <a href={authorUrl} className="reddit_widget__posted_by" target="_blank" rel="noopener noreferrer">u/{author}</a>      </div>      <a href={url} className="reddit_widget__title" target="_blank" rel="noopener noreferrer">{title}</a>    </div>  )}// Filter, since reddit always returns stickied posts up topfunction nonStickiedOnly(post){  return !post.data.stickied}function App({ domElement }) {  const subreddit = domElement.getAttribute("data-subreddit")  const [loading, setLoading] = useState();  const [error, setError] = useState('');  const [data, setData] = useState([]);  useEffect(() => {    // Fetch data from reddit    setLoading(true)    fetch(`https://www.reddit.com/r/${subreddit}.json`)      .then((response) => response.json())      .then((data) => {        setLoading(false);        setData(data.data.children.slice(0, 10));      })      .catch((e) => {        console.log(e)        setLoading(false);        setError('error fetching from reddit');      });  }, [ subreddit ])  return (    <div className="reddit_widget__app">      <h1 className="reddit_widget__header">        Latest posts in <a href={`https://reddit.com/r/${subreddit}`} rel="noopener noreferrer">/r/{subreddit}</a>      </h1>      <div className="reddit_widget__inner">        {loading && "Loading..."}        {error && error}        {!!data.length && data.filter(nonStickiedOnly).map(renderPost)}      </div>      <p className="reddit_widget__powered_by">        This widget is powered by{" "}        <a          href="https://javascriptpros.com"          rel="noopener noreferrer"          target="_blank"        >          JavaScriptPros.com        </a>      </p>    </div>  );}export default App;

Building our widget
We initialized our app using create-react-app. For the sake of getting our entire bundle into a single JS & CSS file, well build using parcel. Instead of completely replacing our build script, well add a new one called build:widget. In this article, we wont dive too deep into how parcel works, but feel free to check it out.

First, add parcel as a dependency

yarn add --dev parcel-bundler

Update package.json with a new build script. This tells parcel to build our JS (which will also build our css) into our docs directory. Source maps wont be needed, to keep our build small. We chose the docs directory, so that we can publish our widget using GitHub pages, but any directory works.

"build:widget": "parcel build src/index.js --no-source-maps -d docs",

You may also want to ignore the cache directory parcel uses in .gitignore

# .gitignore# parcel .cache

The full code, including styling, can be seen here. You can also demo the widget itself here.


Original Link: https://dev.to/sababg/creating-react-widgets-that-can-be-embedded-on-any-website-by-anyone-5fmf

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To