Dynamic component used in render is getting called twice - javascript

I have created a sample test app as below
import React from 'react';
class App extends React.Component {
render() {
const SampleBtn = (props) => {
console.log('button rendered');
return <button>test</button>;
};
return (
<div className="App">
<SampleBtn />
</div>
);
}
}
export default App;
When I run the above app, console.log('Button rendered') is called twice. I tried to remove <React.StrictMode> also, it is called twice and sometimes it multiplies. How to resolve this and can I know the reason behind this?
I have used HTML button and create a stackblitz code as below, even then I am seeing the logging twice. Please see the url below
https://react-xd9pzh.stackblitz.io

Maybe add a render method to the class and move the CustomButton outside the class, a working code will help you to find the issue.
Edit: first code example was unclear to me, like others said, probably it's StrictMode. Keep in mind that it will run only in the development environment. For a quick check on your local machine, for example, if you're using create-react-app you can simply run the build command and then serve the app in your browser to verify if you get a double console.log with the prod build.

Related

too much render with react markdown

I am using React Markdown (https://www.npmjs.com/package/react-markdown) to render markdown content in my NextJS project.
When I refresh I have two "toto" & "titi" in my terminal... It is normal or what's wrong with this code?
import Head from 'next/head';
import ReactMarkdown from 'react-markdown';
function Section ({ data }) {
const content = JSON.parse(data.markdown);
const {
title,
sortContent
} = data;
console.log('toto');
return (
<>
<main>
<h1>{title}</h1>
<h1>{sortContent}</h1>
<ReactMarkdown source={content.default} escapeHtml={false} />
</main>
</>
)
}
export async function getServerSideProps (context) {
const json = await import('../../content/article1/data.json');
const content = await import('../../content/fr/article1/content.md');
console.log('titi');
return {
props: {
data: {
title: json.title_content,
sortContent: json.short_content,
markdown: JSON.stringify(content)
}
}
}
}
export default Section
It's part of Reacts development tooling, StrictMode. It is expected and only applies in development mode. You can remove the StrictMode to see it only render the expected number of times, but obviously you lose some development tooling. This tooling can warn you about certain unsafe or unwise practices you might want to avoid such as using legacy APIs.
More details here:
Reactjs Docs
A blog with a good overview
If this is truly the only code you have, then it looks like it's normal. You may have other code that uses these components and that's why in shows twice. But based off the code you have right there, there's no bug.
This is a known side-effect of using React.StrictMode, only in debug mode. You can read more about this here.
Strict mode can’t automatically detect side effects for you, but it
can help you spot them by making them a little more deterministic.
This is done by intentionally double-invoking the following functions:
Class component constructor, render, and shouldComponentUpdate methods
Class component static getDerivedStateFromProps method
Function component bodies
State updater functions (the first argument to setState) Functions passed to useState, useMemo, or useReducer

How to live edit a class/functional component on Storybook

Hello i m using storybook/react library to create stories of my components.
So far so good, I followed the tutorial on https://www.learnstorybook.com/react/en/get-started and i have add stories on the left side of the screen with the add command, like so:
add('archived', () => <Task task={{ ...task, state: 'TASK_ARCHIVED' }} {...actions} /> );
The Task component is a functional component.
I 'm also using the storybook-addon-react-live-edit storybook addon, from https://github.com/vertexbz/storybook-addon-react-live-edit for having live edit on the stories, like so:
The code for the above example is this:
``
stories.addDecorator(withLiveEditScope({ React, Test }));
stories.add('simple JSX', withLiveEdit(return <div>hello there!!</div>, {color: 'red'}))`
This code is simple because i just show jsx code.
Problem
I want to live edit a functional or class component, from another file but the functions withLiveEdit(source[, scope]) and addLiveSource(name, source[, scope]), accept only string as the source.
So if i add the story like so: stories.addLiveSource('demo', return ${Test});
Test is a separate Test.js file:
const Test = class Welcome extends React.Component {
render() {
return <h1>Hello, world!!</h1>;
}
}
export default Test;
Results is, it shows the code on 'live tab', but its not actually rendered on the top window.
So my question is, how can i import class or functional components on addLiveSource()/withLiveEdit()
Thanks.
I hit the same issue, you can do this:
storiesOf(group, module)
.addDecorator(withLiveEditScope({ React, SomeFunctionComponent, StyledHeader }))
.addLiveSource("title", `<SomeFunctionComponent><StyledHeader>hello</StyledHeader></SomeFunctionComponent>`);
Basically anything you use inside the template literal in addLiveSource which would need an import you need to add to withLiveEditScope.
The only drawback is you have to write it all inside the template literal (P.S. if you solve that limitation please let me know!)

Debugging why react-native release build crushes and debug one works fine

I was working in Debug mode where everything seemed fine, however when I build my app to TestFlight (i.e. release build) it crashed at LaunchScreen.
After some time and debugging I narrowed issue down to following code
import React from 'react'
import { Subscribe } from 'unstated'
const getStoreAsProps = (storeArr) => {
const storeProps = {}
storeArr.map(value => (storeProps[value.constructor.name] = value))
return storeProps
}
const withStore = (...args) => (Element) => () => (
<Subscribe to={[...args]}>{(...args) => <Element {...getStoreAsProps(args)} />}</Subscribe>
)
export default withStore
The way this works is as follows
import React from "react"
import { Text } from "react-native"
import AuthStore from "./store/Auth"
import RouterStore from "./store/Router"
import withStore from "./store"
class MyComponent extends React.Component {
render() {
const {AuthStore, RouterStore} = this.props
return <Text>{AuthStore.state.username} {RouterStore.state.pathname}</Text>
}
}
export default withStore(AuthStore, RouterStore)(MyComponent)
So essentially withStore is a higher order component that can take in any number of arguments, in this case Stores and pass them to Subscribe component (this is part of state management library I am using called unstaded) which in turn returns render props that are then passed to my component as props.
It works fine in Debug mode, but I get error like this in Release mode
undefined is not an object while evaluating e.state
This error from XCode debug logs.
I think something somewhere during Release build is different compared to Debug one that makes this.props.AuthState for example [undefined] and error is thrown when I am specifying <Text>{AuthStore.state.username} {RouterStore.state.pathname}</Text> where my Store props are undefined, hence I can't access their state.
I'd love to keep this Higher Order Component I made for store, as it enables really nice dev experience, but need to be able to debug what exactly breaks in release build, which thus far I was not able to do.
What optimisations are made during release build that could have effect here?
Answer originates from: https://github.com/facebook/metro/issues/182#issuecomment-398052619
Issue was in accessing value.constructor.name where I was assuming it will always be the same as my Container name. During minification these change, hence it was undefined, adding unique identifier to a class resolves the issue

Import or require a bundled js file created by react application

Let's say I have a normal react application using redux and some ajax calls.
If I want to pass it to someone I will give them the bundled js file I created with webpack and ask them to include it in their HTML + render a div with an id of "myApp" for example:
<div id="myApp"></div>
Ok, what if their website is also created with react, and they want to include my bundled js file inside one of their components, and of course render the relevant div?
I tried to use import or require to simulate this:
require('./path/to/myBundle.js');
import './path/to/myBundle.js';
Example:
//...
import './path/to/myBundle.js'; // the file that will render myApp to the relevant div
// ....
export function SomeApp(args){
return(
<div>
<div id="myApp"></div>
<SomeComponent />
</div>
);
};`
This does not work as I get some errors about:
Uncaught Error: Minified React error #37; visit
http://facebook.github.io/react/docs/error-decoder.html?invariant=37
for the full message or use the non-minified dev environment for full
errors and additional helpful warnings.
And when I visit this site I see:
_registerComponent(...): Target container is not a DOM element.
However, if they'll use this file (myBundle.js) outside their components (top level index.html for example) it will work just fine of course.
EDIT:
I forgot to mention that I think I know what the problem is, the application doesn't have the HTML ready with this div yet. but I don't know a good and native way to wait for it to exist.
EDIT #2 following #Frxstrem 's answer:
I'm trying to follow this answer but I think I'm doing it wrong.
I have 2 copies of corry house slingshot demo app as app1 and app2.
changed the 'output' on webpack.config.prod.js of app1 to:
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
filename: 'app1Bundle.js',
library: "App1",
libraryTarget: "umd"
},
I'm trying to render app1 inside the homepage component of app2.
so i copied the "published" files from app1 to the root of app2 and called the folder app1, then added an import call:
import {app1} from '../../app1/app1Bundle';
and a matching tag inside the return function:
const HomePage = () => {
return (
<div>
<app1 />
<h1>App 2</h1>
</div>
);
};
I get the same error as I posted above.
I also tried different combinations:
import app1 from '../../app1/app1Bundle'; // without curly braces
or even just getting the script as a normal js script
import '../../app1/app1Bundle';
or
require('../../app1/app1Bundle');
and then tried to render a normal div tag with an id of "app1"
const HomePage = () => {
return (
<div>
<div id="app1"></div>
<h1>App 2</h1>
</div>
);
};
nothing seems to work as I still get the same error.
I think the problem is the timing of the script load and the rendering of the elements. I think the div does not exist yet when the bundled script is searching for it.
By default, Webpack will expose the entry module as a variable, which is useful when you include scripts with a <script> tag. (Because of this, if you require it you would likely just get {}.) However, if you want to load your bundle from other modules, you'll need to tell Webpack to expose it as an exported module instead.
The easiest way to do this is to set
{
...
"libraryTarget": "umd"
}
in your Webpack configuration. With that, Webpack knows that it should expose your entry module as a module that can be required in Webpack, but can also be loaded with a <script> tag as necessary.
Webpack libraryTarget documentation
The main problem i faced was to include the bundled js file of app1 after the DOM contains the target div it needs.
What i ended up doing was, creating a component in app2 project that will require() the bundled js file on componentDidMount() and will render and return the target div with a relevant id.
The reason i created a component is purely for re-usability purpose, instead of requiring this script with componentDidMount() on every component that needs it.
So, this is the component:
import React from 'react';
class AppOne extends React.Component {
componentDidMount() {
require('../app1/app1Bundle.js');
}
render() {
return (
<div id="app1"></div>
);
}
}
export default AppOne;
And this is how i use it in other component:
import React from 'react';
import AppOne from './AppOne';
const HomePage = () => {
return (
<div>
<h1>App 2 - wrapper for app1</h1>
<hr />
<AppOne />
<hr />
<h1>This is App2 as well </h1>
</div>
);
};
export default HomePage;
It's working fine. my only concern is that i may face some conflicts with react because i'm using 2 react apps though for ow i don't see any errors.
I guess that's an issue for a different question.
EDIT:
If someone will use this approach you should note that this will work only for the first load. because after the component will re-render itself the bundled script will not run again.

Errors with clipboard.js in React component?

I’m trying to use clipboard.js in a React component, and it causes my devserver to start failing with the Node error:
ReferenceError: Element is not defined
at Object.<anonymous> (/mnt/home/me/code/board/webapp/node_modules/matches-selector/index.js:6:13)
I initialize the clipboard in componentDidMount but am still getting this error. I actually think the error may have something to do with my import, because even when I don’t actually initialize the clipboard (but include the import) I get the error. Does anyone have an idea what I might be doing wrong?
Relevant code (styling excluded):
import React, { Component } from 'react';
import Clipboard from 'clipboard';
export default class CodeSnippet extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
new Clipboard('.copyButton', {
target: () => document.getElementById('snippet')
});
}
render() {
return (
<div style={styles.snippetCopy}>
<div id="snippet" style={styles.snippet}>
{'this text will copy'}
</div>
<button
className={"copyButton"}
id="clipper"
data-clipboard-text='snippet'
style={styles.buttonStyle}
text={'Copy code'}>
</button>
</div>
);
}
}
You can't require clipboard.js if you're doing server side rendering. It's annoying but instead of installing via npm, they suggest including the script manually like this:
<script src="https://cdn.jsdelivr.net/clipboard.js/1.5.12/clipboard.min.js"></script>
https://github.com/zenorocha/clipboard.js/issues/157
I created a fiddle updating your code. It's a suggestion of integrating clipboardjs and React, using ref's and clipboardjs' text function.
Check here: https://jsfiddle.net/mrlew/L54ky6hj/

Categories