How to break out of Next/React import hell? - reactjs

After using Vue and Nuxt for more than a year, I decided to learn React and Next.js and almost immediately noticed the horrible Developer Experience.
Every stylesheet and component needs to be imported so there's always bloated import hell at the start of each component.
Not to mention if you need an extra library as you can't hook into any global object like Nuxt's this.$plugin option.
Is there some package to manage these imports for Nextjs? As far as I know, everyone who uses it doesn't mind it and that's what surprises me.
This Question may come as an insult to React and it is, but I just want at least one reason to join the hype-train as to why React is more popular.

create a file in pages directory named it _doucument.js or _document.ts (for TypeScript) and import React in it like below :
(below codes are TypeScript)
import React from 'react';
import Document, {
DocumentContext,
Head,
Html,
Main,
NextScript,
} from 'next/document';
export default class CustomDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
render() {
return (
<Html lang="en">
<Head>
<title>Your title</title>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
and any more doesn't require that import React in any components.

Related

React test fails becasue target container is not a DOM element

I'm trying to run component tests and the tester keeps on failing because the target container is not a DOM element. I found an answer that recommended removing exported values from index.tsx but it did not fix the issue.
src/Component/__tests__/FirstPage.test.tsx
● Test suite failed to run
Target container is not a DOM element.
8 | export const socket = io('http://localhost:8080');
9 |
> 10 | ReactDOM.render(
| ^
11 | <React.StrictMode>
12 | <App/>
13 | </React.StrictMode>,
at Object.render (node_modules/react-dom/cjs/react-dom.development.js:26091:13)
Here is my App.tsx file
import React, { Component } from "react";
import './App.css';
import FirstPage from './Component/FirstPage/FirstPage';
export default class App extends Component {
render() {
return (
<FirstPage/>
);
}
}
Here is the index.tsx file:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import io from 'socket.io-client';
import App from './App';
import reportWebVitals from './reportWebVitals';
export const socket = io('http://localhost:8080');
ReactDOM.render(
<React.StrictMode>
<App/>
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint
reportWebVitals();
And here is the test
import { render, screen } from '#testing-library/react';
import userEvent from '#testing-library/user-event';
import React from 'react';
import FirstPage from '../FirstPage/FirstPage';
import User from '../User/User';
const testUser = new User("testUser", "id")
test('Test if FirstPage includes enter lobby button', () => {
render(<FirstPage/>);
const textElement= screen.getByText(/Enter Lobby!s/i);
expect(textElement).toBeInTheDocument();
});
Here is my index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"/>
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000"/>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Edit
After spending some time on your project I found out what was causing this issue with your tests. In some of the components that FirstPage is using, you were importing socket like that
import { socket } from "../.."
This caused unexpected behaviour in the tests because the path was wrong.
To fix this, I took the socket export from index.tsx and put it in App.tsx and wherever you need to import it you can just do lit like this
import { socket } from "../../App"
That resolves the issues with the tests. Also, the improvements mention above still apply for cleaner code .
First of all, in your App.tsx you are importing FirstPage and name it as Greeting but then you are returning FirstPage in your component. You need to either return Greeting or rename the import to FirstPage. Also, you need to import React in order for the code to work. Even if you don't use React explicitly, you still need to import it because JSX is tranpiled to React.createElement() which uses React.
// App.tsx
import React, { Component } from "react";
import FirstPage from "./Component/FirstPage/FirstPage";
export default class App extends Component {
render() {
return <FirstPage />;
}
}
Moving on to your test, it's a good practice to put all the tests inside __tests__ folder. Also, it looks like you don't actually import FirstPage as well and then try to use it. Try importing it and running the tests again with the proposed changes in the App.tsx.
// /Components/FirstPage/__tests__/FirstPage.test.tsx
import React from "react";
import { render, screen } from "#testing-library/react";
import FirstPage from '../FirstPage';
test("Test if FirstPage includes enter lobby button", () => {
render(<FirstPage />);
...
});
I've tried it in the codesandbox and the tests worked.

React: why set the document title inside useEffect?

I just watched a talk from React Conf 2018. In the video, the speaker shows 2 ways to set the document title. The first is by using the Lifecycle methods (componentDidMount and componentDidUpdate) if we use a class component, the second one is by using the useEffect hook if we use a function component. It looks like that's the recommended way to do it according to answers from this question.
But, I tested the following code and it seems to work just fine to set the document title directly:
import React from 'react';
import ReactDOM from 'react-dom';
function App() {
document.title = 'wow'
return <p>Hello</p>
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
The title changed:
Is there any significance of setting the document title inside useEffect or componentDidMount?
Or is it because it was the only way to set the document title?
Is it okay to set the document title directly like I did in the snippet above?
Update:
It also works with class component:
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
render() {
document.title = 'wow'
return <p>Hello</p>
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
The thing is, the render function in React, is considered to be a pure function.
A pure function should not have any side-effects. Eventually updating the document.title means that you are referencing the document directly from render-> which is already considered a side effect.
Render should mainly do one thing -> render some JSX, if there is a need to do some API calls/interact with the document/window etc..., this should be placed in componentDidMount/componentDidUpate for a class Component, OR in a useEffect/useLayoutEfect for a functional component based on hooks.
(useEffect for e.g. is a function that runs asynchronously, and updating the document title from it will not block the rendering, whereas doing it directly will block it.)
Use react-helment which is also widely used in other frameworks with the same name as helmet express ...
Here is the same code:
import {Helmet} from "react-helmet";
class Application extends React.Component {
render () {
return (
<div className="application">
<Helmet>
<meta charSet="utf-8" />
<title>My Title</title>
<link rel="canonical" href="http://example.org/example" />
</Helmet>
...
</div>
);
}
};
Another way to use via props to which is cleaner IMO
...
<Helmet titleTemplate={`%s | ${props.title}`} defaultTitle={constants.defaultTitle} />
You may also use react-helmet-async for more features.

React helmet or next/head for Next Js project?

I am making a next js application (React SSR), and now I am into implementing the meta tags in head.
So for now I have used next/head in _app.tsx file like,
import React from 'react';
import App from 'next/app';
import Head from 'next/head';
import { ThemeProvider } from '#material-ui/core/styles';
import CssBaseline from '#material-ui/core/CssBaseline';
import theme from '../src/theme';
export default class MyApp extends App {
componentDidMount() {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentElement!.removeChild(jssStyles);
}
}
render() {
const { Component, pageProps } = this.props;
return (
<React.Fragment>
<Head>
<title>My page</title>
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
</Head>
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<Component {...pageProps} />
</ThemeProvider>
</React.Fragment>
);
}
}
And the whole working code can be found here at sandbox: https://codesandbox.io/s/interesting-tereshkova-217ks
I am just to know whether using next/head itself enough in next js application or else need to implement react-helmet ??
If the react-helmet is needed then kindly help me how to implement in the provided next js application.
I am new into Next Js and SSR, So please help me in right direction which is the best method to achieve the result.
I am just to know whether using next/head itself enough in next js application or else need to implement react-helmet ??
react-helmet makes sense to use if you're rolling your own server side rendering solution and are not using Next.js. As far as I know, next/head is bascially a built-in version of react-helmet and does everything react-helmet does.
So no, you don't need to use react-helmet if you are using Next.js. Just use next/head.
If the react-helmet is needed then kindly help me how to implement in the provided next js application.
That said, if you want to use react-helmet with Next.js, here is an example: https://github.com/zeit/next.js/tree/canary/examples/with-react-helmet. Not sure why you'd do this, but the example exists. There is some discussion about it.
By the way, just like react-helmet, you can use next/head anywhere in your render tree—not just the App component like in your example—and all the tags will be aggregated into the <head> tag at the top.
There is also a great package for Meta tags for Next JS
next-seo

React Component not starting up on the project

I just started working in ReactJS and I was going through a basic React tutorial.
I have created a project and added Babel, React, ReactDOM, Webpack, Express as the dependencies.
Now I have this Counter.js :
import React, {Component} from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = {count: 0};
}
render() {
return (
<button onClick={() => {this.setState({ count: this.state.count + 1 });}}>
Count: {this.state.count}
</button>
);
}
}
export default Counter;
And the main.js (which is defined as the entry point of the application in webpack.config.js file):
import React from 'react';
import ReactDOM from 'react-dom';
import Counter from './Counter.js';
ReactDOM.render(<Counter/>,document.getElementById("warbox"));
The PROBLEM is that when I start up the application, the component is not rendered on the page AND also the React DevTool Chrome extension doesn't light up (if that makes any sense).
Can't seem to figure out the problem exactly. Any help is appreciated. Apologies if its too basic.
The index.html :
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="warbox"></div>
<script src="bundle.js"></script>
</body>
</html>
UPDATE : Solved. I think I should update what made it work for others to see. Heres the problem I had : Apparently the bundle.js needs to be updated by running the webpack explicitly. After making the changes, I had to stop the server and run the webpack and restart the server. It WORKED!

Basic react component not rendering

i'm using spring boot and my index.html is in src/main/resources/templates directory and below is the content. If i render a static content from html itself it renders but when i try to render from react component it doesn't render anything
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
<meta charset="UTF-8"/>
<title>ReactJS + Spring Data REST</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react-dom.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
</head>
<body>
<div id="react"></div>
<script src="built/bundle.js"></script>
</body>
</html>
my react component is in src/main/js/ directory and app.js file
Below is the all the codes i have in the app.js file
import React from 'react';
import {render} from 'react-dom';
import RendorTest from 'components/RendorTest';
class RendorTest extends React.component{
rendor(){
return(
<div><h1>Spring Boot + Rest + React.js</h1></div>
);
}
}
var element = <RendorTest />;
ReactDOM.render(
element,document.getElementById('react')
)
I'm not sure why you're importing RendorTest and then declaring another class of the same name, but you are also extending the wrong method on the React object. You need to extend React.Component { } not .component.
You could also import React, { Component } from "react"; and then extend Component { }
As ahutch mentioned, you also need to call the render() method, rendor() is not a method of React.Component.
Kyle is right and also you want to call render() and not rendor(). This component is also probably better written as a stateless functional component, for example:
const RendorTest = (props) => {
return (
<div>
<h1>Spring Boot + Rest + React.js</h1>
</div>
)
}

Resources