How to use leader-line (an external javascript library) in react? - javascript

I want to use leader-line in my React web project. It is an external javascript library, but I don't know how to integrate it into the project with the JSX syntax.
For example, its documentation tells us the general implementation:
Html
<div id="start">start</div>
<div id="end">end</div>
Javascript
// Add new leader line from `start` to `end` (HTML/SVG elements, basically).
new LeaderLine(
document.getElementById('start'),
document.getElementById('end')
);
How should I write in JSX file?
I try to write below, but failed.
import React, { Component } from 'react';
import LeaderLine from 'leader-line'
class Page extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
new LeaderLine(document.getElementById('start'),
document.getElementById('end'));
}
render() {
return (
<div className="Page">
<div id="start"></div>
<div id="end"></div>
</div>
)
}
}
export default Page;
This is the npm package page of leader-line.

Depending on what you are trying to achieve with leader-line, you may find that you can achieve it just as well with react-xarrows.
https://www.npmjs.com/package/react-xarrows
React-xarrows can be integrated into a React app much more easily (even using DOM identifiers rather than React Refs, if you prefer).
See this example code (taken directly from the link above), showing usage.
import React, { useRef } from "react";
import Xarrow from "react-xarrows";
const boxStyle = {
border: "grey solid 2px",
borderRadius: "10px",
padding: "5px",
};
function SimpleExample() {
const box1Ref = useRef(null);
return (
<div
style={{ display: "flex", justifyContent: "space-evenly", width: "100%" }}
>
<div ref={box1Ref} style={boxStyle}>
hey
</div>
<p id="elem2" style={boxStyle}>
hey2
</p>
<Xarrow
start={box1Ref} //can be react ref
end="elem2" //or an id
/>
</div>
);
}

I've made a small prototype to illustrate how it could be achieved.
class Line extends React.Component {
componentDidMount () {
this.waitWhenRefIsReady();
// scroll and resize listeners could be assigned here
}
componentWillUnmount () {
if(this.timer) {
clearInterval(this.timer);
}
}
shouldComponentUpdate () {
setTimeout(() => {
// skip current even loop and wait
// the end of parent's render call
if(this.line) {
this.line.position();
}
}, 0);
// you should disable react render at all
return false;
}
waitWhenRefIsReady () {
// refs are generated via mutations - wait for them
this.timer = setInterval(() => {
if(this.props.start.current) {
clearInterval(this.timer);
this.drawLine();
}
}, 5);
}
drawLine () {
const {start, end} = this.props;
this.line = new LeaderLine(start.current, end.current);
}
render () {
return null;
}
}
class App extends React.Component {
constructor (props) {
super(props);
this.state = {
left: 0,
};
this.myRef1 = React.createRef();
this.myRef2 = React.createRef();
}
componentDidMount() {
this.animateLine();
}
animateLine() {
setInterval(() => {
const limit = 200;
const {left} = this.state;
const x = ((left % limit) + limit) % limit;
this.setState({left: x + 10});
}, 1000);
}
render () {
const {left} = this.state;
const {myRef1, myRef2} = this;
return <div className="container">
<Line
start={this.myRef1}
end={this.myRef2} />
<div
id="start"
ref={this.myRef1}
style={{
left: `${left}px`
}}></div>
<div
id="end"
ref={this.myRef2}></div>
</div>
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Leader Line + React JSX simple prototype

import LeaderLine from 'leader-line';
Add in leader-line.min.js (at end)
if (module && module.exports) { module.exports = LeaderLine }

Refer to this thread for how to integrate Leaderline into your react project :
https://github.com/anseki/leader-line/issues/8#issuecomment-370147614
in summary,
you cant just do
import LeaderLine from 'leader-line';
to import LeaderLine, because its not an ES2015 module yet!
similar to how #shilpa pointed out,
you can tweak the webpack config to include -
rules: [
{
test: require('path').resolve(__dirname, 'node_modules/leader-line/'),
use: [{
loader: 'skeleton-loader',
options: {procedure: content => `${content}export default LeaderLine`}
}]
}
and then inside componentDidMount, you could do
new LeaderLine(document.getElementById('start'),
document.getElementById('end'));

Related

React.js async rendering: when componentWillMount will be called multiple times

I'm exploring React 16. One of the new features of this version is Async Rendering (aka Fiber). It is said that componentWillMount is unsafe because it can be called multiple times and sometimes it will cause unwanted side effects. I'd read this document https://github.com/acdlite/react-fiber-architecture and watched this video https://www.youtube.com/watch?v=aV1271hd9ew&feature=youtu.be but I can't find working example of such behaviour of componentWillMount.
In this questions:
React componentWillUpdate getting called twice
Why might componentWillMount be called multiple times with React Fibre?
it is said that componentWillMount may be called several times when high priority event occurs during rendering process.
So I tried to make it myself using create-react-app and React.js 16.11.0. My idea is to build big react tree and add css animation on root component.
JSX:
class RecursiveComponent extends React.Component {
componentWillMountCalledTimes = 0;
componentWillMount() {
this.componentWillMountCalledTimes++;
if (this.componentWillMountCalledTimes > 1) {
console.log(`Mounting ${this.props.depth} call ${this.componentWillMountCalledTimes}`);
}
}
render() {
if (this.props.depth > 0) {
if (this.props.depth % 2) {
return (
<div>
<RecursiveComponent depth={this.props.depth - 1} />
</div>
);
} else {
return (
<span>
<RecursiveComponent depth={this.props.depth - 1} />
</span>
);
}
} else {
return <div>Hello world</div>;
}
}
}
class App extends React.Component {
state = {
depth: 1000,
}
componentDidMount() {
setInterval(() => {
this.setState({ depth: 1001 + this.state.depth % 10 });
}, 1000);
}
render() {
return (
<div className={'App'}>
<RecursiveComponent depth={this.state.depth} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
styles:
.App {
width: 400px;
height: 400px;
background-color: red;
animation-duration: 3s;
animation-name: slidein;
animation-iteration-count: infinite;
}
#keyframes slidein {
from {
margin-left: 300px;
}
to {
margin-left: 0;
}
}
I expect smooth animation and rare console output but there is no messages in console. What is my mistake? How can I demonstrate multiple calls of componentWillMount function?
Upd:
As #DoXicK mentioned Async Rendering is disabled by default in current version of React.JS. I'd followed by this guide https://reactjs.org/docs/concurrent-mode-adoption.html and write such example
import React from 'react';
import './App.css';
class Caption extends React.Component {
componentWillMountCalledTimes = 0;
componentWillMount() {
wait(100);
this.componentWillMountCalledTimes++;
if (this.componentWillMountCalledTimes > 1)
console.log(`Mounting ${this.props.depth} call ${this.componentWillMountCalledTimes}`);
}
render() {
return <div>{this.props.children}</div>;
}
}
class App extends React.Component {
state = { counter: 0 }
componentDidMount() {
setInterval(() => {
this.setState({ counter: this.state.counter + 1 });
}, 100);
}
render() {
if (this.state.counter % 10 === 0) {
return <div>Empty</div>;
} else {
return (
<div className={'App'}>
<Caption>{'Hello 1'}</Caption>
<Caption>{'Hello 2'}</Caption>
<Caption>{'Hello 3'}</Caption>
</div>
);
}
}
}
function wait(time) {
const start = Date.now();
while (Date.now() - start < time) {
}
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
I'd used CSS from the example above. I'd expected to get at least one message about multiple call of componentWillMount on one element (because each element renders more than , but I had no luck.
I think I'm missing something but I don't understand what.
I've found example in this article: https://0e39bf7b.github.io/posts/react-journey-componentwillmount-in-concurrent-mode/
let lastCounter = 0; // Global variable for multiple mounts detection
class Counter extends React.Component {
componentWillMount() {
if (lastCounter === this.props.value) {
console.log(`mount counter with value = ${this.props.value} multiple times`);
}
lastCounter = this.props.value;
// Syncronously wait for 100ms to emulate long work
const start = Date.now();
while (Date.now() - start < 100);
}
render() {
return <div>{this.props.value}</div>;
}
}
class App extends React.Component {
state = { counter: 0, showGreetings: true };
componentDidMount() {
this.interval = setInterval(() => {
this.setState({ counter: this.state.counter + 1 });
}, 500);
}
componentWillUnmount() {
clearInterval(this.interval);
}
toggleGreetings = () => {
this.setState({ showGreetings: !this.state.showGreetings });
};
render() {
// Use key attribute to force React.JS to remount Counter component
return (
<>
<button onClick={this.toggleGreetings}>Toggle greetings</button>
{this.state.showGreetings && <h1>Hello world!</h1>}
<Counter value={this.state.counter} key={`counter-${this.state.counter}`} />
<div>{this.state.counter2}</div>
</>
);
}
}
// Instead of regular initialization
// ReactDOM.render(<App />, document.getElementById('root'));
// use Concurrent Rendering for this component
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
My main misunderstanding was the idea that componentWillMount will be called multiple times for the same instance and as it's described in the article new instance of component is created and componentWillMount is called for it. Now the idea is clear.

How can I do manual code-splitting with preact?

I want to do code-splitting manually using preact. Preact already splits code for routes, but I want to do it myself.
My use case is that I am building a tool where a user can add widgets to a dashboard. On the home page I only want to include the code for the widgets that the user has configured, not the ones the user has not used.
So I do not want to have the code for all widgets bundled in the bundle.js, but request it lazily when needed, when rendering the list of widgets.
I have attempted to use the async! syntax, which I saw in some old commits for the boiler plate, but that did not work.
A simplified example of my code
The configuration data
[{ "type": "notes", "title": "Widget 1}, { "type": "todo", "title": "Widget 2"}]
The render function of the list
const Grid = ({ widgets }) => (
<ul>
{widgets.map((widget) => <li key={widget.title}><Widget widget={widget} /></li>)}
</ul>
);
Widget component
Here I have a mapping from type to component:
import notes from widgets/notes;
import todo from widgets/todo;
class Widget extends Component {
widgetMap(widget) {
if (widget.type === 'notes') {
return notes;
}
if (widget.type === 'todo') {
return todo;
}
}
render ({ widget }) {
const widgetComponent = this.widgetMap(map);
return (
<div>
<h1>{widget.title}</h1>
<widgetComponent />
</div>
);
}
}
If you are using Preact X, it features <Suspense> and lazy which is same API React also uses. More about it in depth you can read here: https://reactjs.org/docs/concurrent-mode-suspense.html
Your example, modified would look like this (code adjusted from here):
import { Suspense, lazy } from `preact/compat`;
const notes = lazy(() => import('./widgets/notes'));
const todo = lazy(() => import('./widgets/todo'));
class Widget extends Component {
widgetMap(widget) {
if (widget.type === 'notes') {
return notes;
}
if (widget.type === 'todo') {
return todo;
}
}
render ({ widget }) {
const widgetComponent = this.widgetMap(map);
return (
<Suspense fallback={<div>loading...</div>}>
<div>
<h1>{widget.title}</h1>
<widgetComponent />
</div>
</Suspense>
);
}
}
For older version of Preact, you can put together async loading HOC yourself as long as you have Babel or some other transpiler set up to handle dynamic module loading
export default asyncComponent = (importComponent) => {
class AsyncComponent extends Component {
constructor(props) {
super(props);
this.state = { component: null };
}
async componentDidMount() {
const { default: component } = await importComponent();
this.setState({ component });
}
render() {
const Component = this.state.component;
return Component ? <Component {...this.props} /> : <div>loading...</div>;
}
}
return AsyncComponent;
}

How to access parent ref from child

I have some problem to pass the ref to child element in JSX.
Please, see the following:
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
render() {
return (
<div id="parent" ref={element => (this.parentRef = element)}>
<canvas id="child" width={this.parentRef.offsetWidth} />
</div>
);
}
}
ReactDOM.render(document.getElementById("app"), <App />);
I want to access #parent width from #child. How it is possible?
This is very late, but I have a solution using the latest React hooks and functional components for future viewers. Due to the way how refs work, they do not cause a re-render on value change. However, mozilla added something called ResizeObserver: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver , which watches components for resize.
import React, { useRef, useEffect, useState } from "react";
import ResizeObserver from "resize-observer-polyfill";
export default function App() {
const parentRef = useRef(null);
const [width, setWidth] = useState(0);
useEffect(() => {
const ro = new ResizeObserver((entries) => {
entries.forEach((entry) => setWidth(entry.contentRect.width));
});
ro.observe(parentRef.current);
return () => ro.disconnect();
}, []);
return (
<div ref={parentRef}>
<div> {width} </div>
</div>
);
}
code in action: https://codesandbox.io/s/reverent-galileo-q7np5?file=/src/App.js
In your particular example you're just getting width of an element and passing it to another element.
If you're using latest react version you should checkout new ref's api (https://reactjs.org/docs/refs-and-the-dom.html)
And your example will look something like that
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
width: 0
};
this.parentRef = React.createRef();
}
componentDidMount() {
window.addEventListener("resize", this.onResize);
this.onResize();
}
componentWillUnmount() {
window.removeEventListener("resize", this.onResize);
}
onResize = () => {
this.setState({
width: this.getParentSize()
});
};
getParentSize() {
return (this.parentRef.current && this.parentRef.current.offsetWidth) || 0;
}
render() {
return (
<div id="parent" ref={this.parentRef}>
<canvas
id="child"
width={this.getParentSize()}
style={{ background: "red" }}
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));

How to add a status minipage in reactjs?

I am building a web front-end and I want to have the same status minipage running on each page of my website. This is what I have so far
export default class Status extends Component {
constructor(props) {
super(props);
this.state = {
// some features
};
}
recheck = event => {
// some status check
}
componentDidMount() {
this.interval = setInterval(() => this.check(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<div className="Status">
{/*... some status */}
</div>
);
}
}
and I'd hope to have such component always rendered. Is there any way to do so?
Here is some sample code on how to include your Status component within App.js
Edit: Per your comment I've included some examples of passing props to <Status/>
// App.js
import React, { Component } from 'react';
import Status from './Status'; // Status.js
class App extends Component {
render() {
const bar = "World";
return {
<div className="App">
...some layout
<Status foo="Hello" bar={bar}/>
</div>
}
}
}

Sharing React component logic between separate renders

I want the logic of a component to be accessible to the render method of separate stateless components.
The reason is that the Desktop version of the app will use the same logic and class methods, but the presentation of it will be different.
class Logic {
constructor() {
this.greeting = 'Buongiorno'
}
showCats() {
return 'Mittens and Puss'
}
}
const Desktop = () => {
return <div style={{ fontSize: 30 }}>
{this.showCats()}
{this.greeting}
</div>
}
const Mobile = () => {
return <div style={{ fontSize: 15 }}>
{this.greeting}
{this.showCats()}
</div>
}
So I am trying to 'glue' the class to the functional component.
Can I do this without passing props into the stateless component?
How can the stateless component access the methods and variables inside the Logic class?
I am aware I could make Desktop and Mobile stateful components that extend the Logic class but I am not sure that is the best thing to do.
function Logic(wrappedComponent) {
showCats() {
return 'Mittens and Puss'
}
return (
<wrappedComponent
greetings="Buongiorno"
showCats=showCats
>
{this.props.children}
<wrappedComponent />
)
}
const Desktop = () => {
return <div style={{ fontSize: 30 }}>
{this.props.greeting}
{this.props.showCats()}
</div>
}
export default Logic(Desktop)
const Mobile = () => {
return <div style={{ fontSize: 15 }}>
{this.props.greeting}
{this.props.showCats()}
</div>
}
export default Logic(Mobile)
Higher order components are generally used to keep common functionality among different components.read more about this here https://medium.com/#franleplant/react-higher-order-components-in-depth-cf9032ee6c3e#.do3h4kouk
This task cab be solved by using "higher order component" approach. Your HoC can look like this:
"use strict";
import React, {Component} from "react";
const getDisplayName = (Component) => Component.displayName || Component.name || 'Component';
/**
* Higher order component to inject logic into provided component
*/
export const withLogic = Component => {
class WithLogic extends Component {
//noinspection JSUnusedGlobalSymbols
static displayName = `WithLogic(${getDisplayName(Component)})`;
get logic() {
if (!this._logic) {
this._logic = new Logic();
}
return this._logic;
}
render() {
return <Component {...this.props} />;
}
}
return WithLogic;
};
and its use is a composition pattern, widely used in React:
export default withLogic(Mobile);

Categories