In my mental model, React function components are basically class components stripped down to render method (and this is reinforced by React docs saying that that render should be a pure function of state and props). I assume that render method and Functional components are called at the same phase of React lifecycle so why I can't use hooks in render method? To be more precise, I want to know the technical limitations of class components which disallow hooks usage.
import React from "react";
export default class App extends React.Component {
render() {
const [state, setState] = React.useState(0); // Invalid hook call. Hooks can only be called inside of the body of a function component.
return (
<div>
<h1>Hello World</h1>
</div>
);
}
}
no, we can't use hooks with class component. So, please, read the documentation
Rules of hooks:
✅ Call Hooks from React function components.
✅ Call Hooks from custom Hooks (we’ll learn about them on the next page).
Related
ReactJS is a great library, However, it misses some features which I found in Vue and Angular. These features can be implemented of course in React, however, they require extra code to be written.
Every react component, or every JSX element I should say has the following properties shared, which are given by React to us to consume:
ref
key
I wanted to add extra props:
renderIf
fallback
These props help in a way I can't describe when it comes to conditional rendering and filtering the views based on the logged-in user permissions and roles (and other conditional rendering use cases, of course).
In react, if we wanted to apply these props to our components, we would use a HOC as follows:
// 🍎 Disclaimer: you don't have to understand any of the code written bellow, the general idea is that this is a HOC.
import React from 'react'
import getVal from './getVal'
export default function EnhancedComponent(OriginalComponent) {
return ({ renderIf: renderIf_ = true, override: override_, fallback: fallback_ = undefined, ...props }) => {
const renderIf = getVal(renderIf_)
const override = getVal(override_)
const fallback = getVal(fallback_)
const consumersComponent = <OriginalComponent {...props} />
let render = fallback
if (renderIf) render = consumersComponent
if (override_ !== undefined) render = override
return render
}
}
Where every time you want to apply these props to your components, you would have to wrap every new component you create with EnhancedComponent as follows:
export default EnhancedComponent(function Sidenav(){
return <div> side nav </div>
})
Now, you can use your Sidenav component within your App component as follows:
import Sidenav from './Sidenav'
export default function App(){
return (
<div>
<Sidenav renderIf={(5 + 5 === 10)}/>
<div>etc</div>
</div>
)
}
This API is great, but it has a drawback, which is, every time you want to apply these cool props (renderIf and fallback) you'll have to repeat these steps:
import Enhanced component to your file.
wrap your export with Enhanced component.
What I am looking for, is a method, or a way to inherit, or to add some props to the original react component class, somehow?
In react class components, I can imagine doing this on the React.Component class which we used to extend from in the past
class Car extends React.Component{
constructor(){}
render(){
return <div>I miss you 🌹</div>
}
}
But in react functional component, how can we do that?
I want to apply these props by default everytime I create a new component, without wrapping my components in a HOC everytime.
Does React have a way to do that? To change its defaults ?
getting in valid hook error in a re write of a clients app... (upgrading code)
mobx 6.3.8 mobx react 7.2.1 and mobx-state-tree 5.0.5
React 17.0.1 RN 0.64.3
i feel like the error is here. i googled the code line for use stores and it led me to the deprecated site... i dont know where to find new handling in the https://mobx.js.org/react-integration.html site... what would this be called?
import { createContext, useContext } from "react"
import { RootStore } from "./root-store"
const RootStoreContext = createContext<RootStore>({} as RootStore)
const RootStoreProvider = RootStoreContext.Provider
// hook error here? // export const useStores = () => useContext(RootStoreContext);
error:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
adding more context... rootstore file
import { Instance, SnapshotOut, types } from "mobx-state-tree"
import { creatMediaPlayerModel } from "../../models/media-player"
import { createUserModel } from "../../models/user"
import { createContentModel } from "../../models/content"
export const RootStoreModel = types.model("RootStore", {
mediaPlayerStore: creatMediaPlayerModel(),
userStore: createUserModel(),
contentStore: createContentModel(),
})
export type RootStore = Instance<typeof RootStoreModel>
export type RootStoreSnapshot = SnapshotOut<typeof RootStoreModel>
From that error message:
Hooks can only be called inside of the body of a function component.
You are not calling a hook from inside the body of a function component. So you are breaking the rules of hooks.
According the rules of hooks you can only call a hook from the top level of a react function component. If you are not inside a functional component, then you cannot use a react hook*.
From the docs:
Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls.
That means you need to call useStores from within a functional component.
Something like:
function MyComponent() {
const myStores = useStores()
// render component with data from stores here
return <>{myStore.myData}</>
}
* The exception, sort of, is a custom hook which can call other hooks. Your useStores here is a custom hook. So it's fine to call useContext from that custom hook.
But that custom hook must obey the same usage rules as the built in hooks, so all hooks are called from the body of a function component, there is just function in between.
I am using react 16.13.1 and created react app using create-react-app 3.4.1.
Mounting lifecycle is being called twice for every component.
I created component FirstComponent.js
import React, {Component} from 'react';
class FirstComponent extends Component {
constructor(p) {
super(p);
console.log("FirstComponent constructor called")
}
componentDidMount() {
console.log("FirstComponent componentDidMount called")
}
static getDerivedStateFromProps() {
console.log("FirstComponent getDerivedStateFromProps called")
return null;
}
render() {
console.log("FirstComponent render called")
return (
<div>
Hello World
</div>
);
}
}
export default FirstComponent;
and called FirstComponent from App.js
import React, {useEffect} from 'react';
import FirstComponent from "./FirstComponent";
function App() {
useEffect(()=>console.log("App useEffect Called"));
console.log("App called");
return (
<FirstComponent/>
);
}
export default App;
console output: (ignoring getDerivedStateFromProps warning)
App called
App called
FirstComponent constructor called
FirstComponent constructor called
FirstComponent getDerivedStateFromProps called
FirstComponent getDerivedStateFromProps called
FirstComponent render called
FirstComponent render called
FirstComponent componentDidMount called
App useEffect Called
This is because codesandbox.io runs react applications in strict mode by using <React.StrictMode /> and in strict mode side effects are double-invoked means executed two times, if you unwrap your <App /> component from <React.StrictMode> in index.js it'll work just fine.
Here's how docs define this:
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
For a simple component written like this:
import React from 'react'
const MyComponent = ({text}) => {
return(<div> {text} </div>
}
Is there a syntax for implementing lifecycle method like componentDidMount() or do I have to convert code to React.Component class?
Is there a syntax for implementing lifecycle method like componentDidMount()
No.
do I have to convert code to React.Component class?
Yes.
See https://facebook.github.io/react/docs/state-and-lifecycle.html
In the alpha version of React there is an opt in feature called hooks which will address this. See more on hooks here https://reactjs.org/docs/hooks-overview.html
How to signal to React that a functional component is "pure", as an equivalent of React.PureComponent for component classes?
function C(props) {
return <var>{props.n}</var>
}
without making it a class
class C extends React.PureComponent {
render() {
return <var>{this.props.n}</var>
}
}
As of React 16.6.0, memo has been added, so the answer is now:
const C = React.memo(props => {
return <var>{props.n}</var>
})
Based on the concept of purity in functional programming paradigms, a function is pure if:
Its return value is only determined by its input values
Its return value is always the same for the same input values
There seem two ways to do it for React functional components:
Using memo from react:
import React, { memo } from 'react';
const Component = (props) {
return (
// Component code
)
}
// Wrap component using "memo" HOC
export default memo(Component);
Using pure from recompose:
import React from 'react';
import { pure } from 'recompose';
const Component = (props) {
return (
// Component code
)
}
// Wrap component using "pure" HOC
export default pure(Component);
To #Shubham and #Andrew:
No, functional components are not PureComponents. Functional components will always get re-render if the parent component re-renders. A PureComponent contains a default shouldComponentUpdate() and I think that's what OP wants.
You can use pure provided by recompose to wrap and optimize your functional components:
import pure from 'recompose/pure'
const YourFunctionalComponent = (props) => {
...
}
export default pure(YourFunctionalComponent)
In addition to CodinCat answer.
The author of the library, recomponse, wrote a note on 25th Oct of 2018, where he stated, that what he tried to solve by the library was solved by the React Team through introducing hooks. Not only that, React team added optimization feature like React.memo(), which was named as React.pure() earlier. So, it's time to use React.memo(). Read Official Docs about it