React component doesn't show correct render count [duplicate] - javascript

I am new to react and I am trying to develop a simple web app with it but I get an error.
My Constructor is called twice when I load a class component can you help?
Home.js
import React from 'react'
import Land from "../Land";
function Home() {
return (
<div>
<h1>Home!</h1>
<Land/>
</div>
)
}
export default Home
Partial Land.js
import React, { Component } from 'react'
import Login from "./Login";
class Land extends Component {
constructor(props) {
super(props)
this.state = {
}
console.log("LAND")
}
the log LAND is hit twice.
In some of the components I wish to make an API call that hits a DB but I only want to hit it once.
In many instances using componentDidMount is not convenient because props only appear after componentDidMount therefor id like to do the call in render(I will not be using setState, that would cause a reload of render).
Thanks in advance

You are using <StrictMode/> and it's development mode
While in <StrictMode/>, react will detect unexpected side effects which will call lifecycle functions more than once duration the development mode, will not be trigger twice in production.
From docs:
why is called twice
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
will not call twice
Note:
This only applies to development mode. Lifecycles will not be double-invoked in production mode.

I didn't quite get what are you trying to say in the comment, but you totally can call function that fetches data within componentDidMount hook
Here's an example:
https://codesandbox.io/s/react-calls-constructor-twice-q5gzs

Related

Creating refs outside of component. Is that a bad practice?

I am working on a library which requires exporting a couple of functions for users to call upon. Those functions need access to component ref in order to add/remove classNames and auto scroll etc.
I was able to get it to work by moving my ref (created by React.createRef) outside of the component itself (NOT talking about defining it outside of the constructor but inside the component)
Here's how my code looks like (used a class component instead of functional as the hook useRef obviously can't be used outside)
import React, { PureComponent, createRef } from "react";
import { typingEffect } from "../redux/actions/dispatch";
import { containerRef } from "./Container";
let typingRef = createRef();
export async function displayTypingEffect() {
await typingEffect();
typingRef.current.className += " rcb-is-typing";
containerRef.current.scrollTop = containerRef.current.scrollHeight + 700;
}
export function hideTypingEffect() {
typingRef.current.className = "rcb-typing-container";
containerRef.current.scrollTop = containerRef.current.scrollHeight + 700;
}
export default class Typing extends PureComponent {
render() {
return (
<div ref={typingRef}>
rest of the component code which is unnecessary for this question
</div>
)
}
I am just wondering if there's a possibility of any unforeseen issues or bugs if I follow this pattern.
Thank you.
This makes the typingRef
a global variable (inside the module), and
be created outside of any React life cycles
typingRef will be the same object for every instance of the Typing component, i.e. if two components are created from the Typing class, both will write to the same typingRef. Your API will provide access some DOM element, but you can not be sure which one it currently is.
typingRef is created as soon as the file is imported, before React even starts, and will live for the life time of the Javascript code, not the life time of any React component.
I think (not 100% sure) any DOM elements referenced by typingRef will be kept (at least) until typingRef gets overwritten (or the Javascript execution is ended). So if a Typing component gets unmounted, the DOM element (and everything that's connected to it) is still kept in memory. So your API will provide access to "useless" DOM elements.

setTimeout callback called twice in React functional component?

Consider the following code:
import React from "react";
function App() {
console.log("render");
setTimeout(() => {
console.log("time is up");
}, 2000);
return <div>nothing to see here</div>;
}
export default App;
I expected the following output:
render
time is up
But the real output in the Chrome console is:
Note the 2 before time is up, showing us that time is up was output twice.
I don't understand why time is up is output twice. Can anyone explain this?
The component is rendered twice because CRA sets React's strict mode by default, which among other things tries to help you detect side effects (emphasis mine):
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
So far, this is covered by posts like:
Why my simple react component print console twice?
Why is console.log logging twice in react js?
Why does useState cause the component to render twice on each update?
However, you might then expect both "render" and "time is up" to be logged twice. The reason that doesn't happen, which as far as I can find hasn't been covered yet on SO, is that React was updated to deliberately suppress the duplicate logs:
This disables console.log by temporarily patching the global console
object during the second render pass when we double render in strict
mode in DEV.
This applies only to console.logs directly in the functions mentioned above, which doesn't include setTimeout callbacks, so the second console.log("render") is swallowed but the second console.log("time is up") is not.
I believe the problem here is that you didn't put the setTimeout inside a useEffect. This means that when the app rerenders another timeout will get started and cause the issue you are finding. Try something like this.
import React, {useEffect} from "react";
function App() {
console.log("render");
useEffect(() => {
const timer = setTimeout(() => {
console.log('time is up');
}, 2000);
return () => {
clearTimeout(timer);
}
}, []);
return <div>nothing to see here</div>;
}
export default App;
React.StrictMode is a feature intended to ease devs spot problems related to React Lifecycle
It only happens when you run in development mode and renders with extra rules like rendering components more than once.
I think the documentation does a great job explaining this. StrictMode: React
Understandably, it gets irritating for people notice for the first time!

why my header render every time already use React.memo?

could you please tell me why my header render every time already use React.memo ? I have two section my app and user when I am navigation from my apps to user header re-render why ?
here is my code
https://codesandbox.io/s/fancy-microservice-gl734?file=/src/header.js
import React, { useEffect, useState } from "react";
import { withRouter } from "react-router-dom";
const Header = React.memo(function() {
console.log("HeaderHeaderHeaderHeaderHeader");
return <div>header</div>;
});
export default withRouter(Header);
when you run application it shows apps but when you are click user it show user page ..see console header is rerender
I tried with pure component still same issue
import React, { PureComponent } from "react";
class Header extends PureComponent {
render() {
console.log("HeaderHeaderHeaderHeader");
return <div>header</div>;
}
}
export default Header;
https://codesandbox.io/s/fancy-microservice-gl734?file=/src/test.js:0-202
Unfortunately, what you're trying to do is not how React works.
React.memo allows you to avoid running a child's render function if the parent component is rendering that child with the same props. If reconciliation replaces the parent with a new component, the new parent is not going to reuse the same memoized component even if it's rendering the same child as the last parent.
So in the case of your react-router, when you switch routes, every component will be re-rendered. The DOM may avoid unnecessary updates still, but your render functions will still get invoked.
If you pull the <Header/> component out higher up in the tree, you may be able to avoid your re-renders.

useContext only works in stateless functional component

I'm trying to get to grips with the new useContext function in React. Works great in stateless functionality components. For example:
import React from 'react';
import LocaleContext from '../LocaleContext';
const Link = ({ text, url }) => {
const locale = useContext(LocaleContext);
return (
<a href={`/${locale}/${url}`}>
{text}
</a>
);
};
export default Link;
I also want to use useContext in stateful components, and even non React functions, but when I do so, I get the following error:
Hooks can only be called inside the body of a function component.
The message seems simple enough to understand, but is this really true? I can only use it in a stateless functional component? If so, it seems kind of pointless to me, because it's super easy to use a simple HOC or the traditional method of:
<Locale Consumer>
{locale => (
...
)}
</LocaleConsumer>
So what gives here? I have the latest version of every package in my project. Not sure if it matters but I'm developing a NextJS site here.
If you really want to use classes (i actually came from Angular and i still prefer use classes) you can workaround easily like that:
class ComponentImpl extends React.Component<any> {
constructor(props?) {
super(props);
}
render() {
return (
<div>
CounterButton: <button onClick={() => {this.props.appContext.setCount(this.props.appContext.count + 5)}}>App Counter + 5</button>
</div>
)
}
}
export function Component() {
let appContext = useContext(AppContext);
return <ComponentImpl appContext={appContext}></ComponentImpl>
};
And you just use it: <Component></Component>
The problem is what the error says. React hooks aren't available in class components. Due to differences between class components and function components, hooks cannot be used with the former.
As the documentation says,
Hooks let you use more of React’s features without classes. Conceptually, React components have always been closer to functions. Hooks embrace functions, but without sacrificing the practical spirit of React. Hooks provide access to imperative escape hatches and don’t require you to learn complex functional or reactive programming techniques.
Hooks are supposed to address common use cases that are specific to class components which couldn't be previously implemented with stateless functional components alone. Functional components aren't stateless since React 16.8 and are allowed to have a state and trigger own updates.
As for useContext hook,
When the provider updates, this Hook will trigger a rerender with the latest context value.
It would be messed up in class component due to difference between functional and class components. Component function is called each time the component is rendered:
const Foo = props => {
const context = useContext(Context);
// use context
}
There's no place in class component that would behave the same way except render function. And if lifecycle-specific tasks go to render function, this means that a class was a wrong choice, and class component needs to be refactored to a function. A counterpart to useContext in class components is contextType, which is currently restricted to single context.
For multiple contexts it's still required to receive them through context Consumer inside render, or as props from higher-order component wrapper:
const contextsHOC = (contexts = {}) => Comp => (
props => {
const contextProps = {};
for (const prop in contexts) {
// eslint-disable-next-line react-hooks/exhaustive-deps
contextProps[prop] = React.useContext(contexts[prop]);
}
return <Comp {...props} {...contextProps}/>;
}
);
#contextsHOC({ bar: BarContext, baz: BazContext });
export default class FooComponent extends Component {
// contexts are mapped to this.props.bar and this.props.baz
...
}
// or
class FooComponent extends Component { ... }
export default contextsHOC({ ... })(FooComponent);
Passing contexts as props allows for additional optimization with PureComponent or shouldComponentUpdate.
useContext is a hook that consumes a context and can only be used in functional components.
If you want to consume context in class components, you will need to look at alternative methods such as Consumer Component, official docs for this here

Using higher order components with Redux containers

First, some context.
I'm using Redux to manage authentication state of my app and have Auth as a Redux container (or smart component).
I've created a wrapper (a higher-order component) that takes Auth and returns it:
export default function AuthWrapper(WrappedComponent) {
class Auth extends Component {
... <Auth stuff here> ...
}
return connect(mapStateToProps, mapDispatchToProps)(Auth);
}
It seems to me that in order to use the wrapper, I just need to invoke it with a component I want to have behind my auth. For example, let's say I'm authenticating a component called UserPage with the wrapper, à la:
const AuthenticatedUserPage = AuthWappper(UserPage)
However, when I use the wrapper like this, React isn't happy with me. I get the following error:
Warning: AuthenticatedApp(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
My best guess is that it doesn't like the connect-ified component that Redux will create when I return it from AuthWrapper... which leads me to my question:
Does React support higher-order components when those components create Redux containers? And if so, why would React be throwing this error?
Here's my two cents. I think the error is occurring elsewhere.
According to this simplified version of the connect function in react-redux, the connect function is simply returning another react component. So in your case, you're returning a component, wrapped inside another component, which is still valid. A container is basically a component.
Read https://gist.github.com/gaearon/1d19088790e70ac32ea636c025ba424e for a better understanding of the connect function.
I also tried the following in my own application and it worked.
import Layout from '../components/Layout'
//Do some other imports and stuff
function wrapper(Layout) {
return connect(null, mapDispatchToProps)(Layout);
}
export default wrapper()
Like the error states, you might just simply be returning an invalid component somewhere in your app. Your app might be throwing the error because you're not wrapping a return call in parentheses on your render method.

Categories

Resources