Can someone help me convert this to react hooks I believe it would be react useState()
https://github.com/react-grid-layout/react-grid-layout/blob/master/test/examples/7-localstorage.jsx
import React from "react";
import RGL, { WidthProvider } from "react-grid-layout";
const ReactGridLayout = WidthProvider(RGL);
const originalLayout = getFromLS("layout") || [];
/**
* This layout demonstrates how to sync to localstorage.
*/
export default class LocalStorageLayout extends React.PureComponent {
static defaultProps = {
className: "layout",
cols: 12,
rowHeight: 30,
onLayoutChange: function() {}
};
constructor(props) {
super(props);
this.state = {
layout: JSON.parse(JSON.stringify(originalLayout))
};
this.onLayoutChange = this.onLayoutChange.bind(this);
this.resetLayout = this.resetLayout.bind(this);
}
resetLayout() {
this.setState({
layout: []
});
}
onLayoutChange(layout) {
/*eslint no-console: 0*/
saveToLS("layout", layout);
this.setState({ layout });
this.props.onLayoutChange(layout); // updates status display
}
render() {
return (
<div>
<button onClick={this.resetLayout}>Reset Layout</button>
<ReactGridLayout
{...this.props}
layout={this.state.layout}
onLayoutChange={this.onLayoutChange}
>
<div key="2" data-grid={{ w: 2, h: 3, x: 8, y: 0 }}>
<span className="text">5</span>
</div>
</ReactGridLayout>
</div>
);
}
}
function getFromLS(key) {
let ls = {};
if (global.localStorage) {
try {
ls = JSON.parse(global.localStorage.getItem("rgl-7")) || {};
} catch (e) {
/*Ignore*/
}
}
return ls[key];
}
function saveToLS(key, value) {
if (global.localStorage) {
global.localStorage.setItem(
"rgl-7",
JSON.stringify({
[key]: value
})
);
}
}
if (process.env.STATIC_EXAMPLES === true) {
import("../test-hook.jsx").then(fn => fn.default(LocalStorageLayout));
}
trying my best to understand react classes since I only know react hooks so any patince help would be so amazing and helpful please and thank you
React classes are simple if you understand the lifecycle of a component.
A component is instantiated, then mounted (associated with DOM) & then it renders.
After every update (state or props) it's re-rendered.
Constructor
The instantiation of a component or any JS class happens in the constructor. It is run only once.
Since class components inherit from React.Component, they pass the props to React.Component by calling super(props). This initializes the props field.
super calls should be the 1st line in a constructor.
You cannot access the following in the constructor: window, document, fetch, localStorage, sessionStorage.
render
The render method returns the rendered Element/Fragment. It corresponds to the returned part of a function component. It runs every time the component is rendered.
Event listener binding
The hardest part of classes is the event method binding.
The implicit this object, is necessary to access state, props & other component methods. However, inside an event listener method*. it refers to the event target. Hence the bind calls. Nevertheless, this problem, can be bypassed by using arrow methods as the arrow functions do not have their own this (ie. this does not refer to event target).
Function components don't have this problem, as they don't need this.
State
The state is initialized in the constructor.
Unlike function components, classes treat the state as a single object.
Instead of,
let [x, setX] = useState(0); let [y, setY] = useState(1);
In classes it's:
this.state = {x:0, y:1};
And to update state, we use the setState method,
this.setState({x: 3}); // updates only x, & y is unchanged: setX(3)
this.setState({x: 3, y: 4}); // updates both x & y: setX(3); setY(4)
this.setState((state)=>({ y: state.y - 1)) // updates y using previous value: setY(y=>y-1)
An advantage of classes is that we can update a state property using another state property's previous value.
this.setState((state)=>({x: (y%2)? 100: 50})); // updates x using y's value
Also, the state can be updated with respect to props:
this.setState((state,props)=>({x: state.x + props.dx}));
In addition, setState has an optional 2nd callback argument. This callback is run immediately after the state update.
this.setState(state=>({x: state.x+1}), ()=> {
// called after x is updated
sessionStorage.setItem('x', this.state.x);
});
// function version
setX(x=>x+1);
useEffect(()=> {
localStorage.setItem('x', x);
}, [x]); // runs every time x is updated
Mounting
After mounting, componentDidMount() is called. Now, component can access the window & document objects.
Here here, you can
Call setInterval/setTimeout
Update state with API calls (fetch) or storage (localStorage & sessionStorage)
It's equivalent to useEffect(()=> {}, [])
Unmounting
Before unmounting componentWillUnmount() is called. Typically, clearTimeout/clearInterval are called here to clean up the component.
It's equivalent to the returned method of useEffect.
useEffect(()=>{
//....
return ()=> {
// componentWillUnmount code
};
}, [])
Related
Redux specifies that "The key to updating nested data is that every level of nesting must be copied and updated appropriately."
// changing reference in top level
const newState = {...oldState}
newState.x.y.z = 10;
setState(newState)
// or updating reference in all levels
const newState = {...oldState}
newState.x = {...newState.x}
newState.x.y = {..newState.x.y}
newState.x.y.z = 10
Is this is same for react, can't find the documentation regarding this on react.
Yes, you should apply this concept when updating your state in React also. If you don't, you can have issues with components not re-rendering when you update your state. Normally, React will rerender your component when your state changes, in both of your examples you're doing this, as you're creating a new object reference at the top-level which will cause React to rerender your component and any of its child components (ie: the children components also get rerender when the parent component rerenders). However, there is an exception to this. If you've memoized a component to help with efficiency using React.memo(), your memoized child component will only update when the props that it's passed change. If you are passing a child component an object that is nested within your original state, React won't rerender that component unless that object reference has changed. This could lead to issues if your memoized children components are trying to use state from your parent component.
For example:
const { useState, memo } = React;
const App = () => {
const [state, setState] = useState({
x: {
y: {
z: 0
}
}
});
const changeStateBad = () => {
// Your first technique (can cause issues)
const newState = {...state};
newState.x.y.z += 1;
setState(newState);
}
const changeStateGood = () => {
// Your second technique for updating at all levels (just rewritten slightly)
const newState = {
...state,
x: {
...state.x,
y: {
...state.x.y,
z: state.x.y.z + 1
}
}
};
setState(newState);
}
return <div>
<NumberDisplay x={state.x} />
<br />
<button onClick={changeStateGood}>Good update</button>
<br />
<button onClick={changeStateBad}>Bad update</button>
</div>
}
// A memoized component that only rerenders when its props update
const NumberDisplay = memo(({x}) => {
return <h1>{x.y.z}</h1>;
});
ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
data types in JS can be divided into primitive data types (eg. string, number) and non-primitive data types (eg. object),
primitive data types can not be modified (eg. using String.prototype.replace will always return a new instance of a string) while non-primitive data types can - and the reference pointing to that data will be "updated" as well (that's a big simplification, but executing eg. x.y = 2 will not create a new instance of x - instead the x will be keept the same in every place it's referenced),
which means to detect a change in the new version of the state (in a most performant way) it is required to create a new instance of an Object, Array (and other non-primitive data type representations)
// changing reference in top level
const newState = { ...oldState }
// changing nested reference
const newState = {
...oldState,
x: {
...oldState.x,
y: 2 // new "y" value
}
}
// changing nested reference, another example
const newState = {
...oldState,
x: {
...oldState.x,
y: {
...oldState.x.y,
z: 'test' // new "z" value
}
}
}
you can read more how change is detected on a basic level here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#comparing_objects
My code has a component that takes both props and has its own internal state.
The component should rerender ONLY when its props change. State changes should NOT trigger a rerender.
This behaviour can be implemented with a class based component and a custom shouldComponentUpdate function.
However, this would be the first class based component in the codebase. Everything is done with functional components and hooks.
Therefore I would like to know whether it is possible to code the desired functionality with functional components.
After a few answers that didn't approach the real problem, I think I have to reformulate my question. Here is a minimal example with two components:
Inner takes a prop and has state. This is the component in question. It must not rerender after state changes. Prop changes should trigger a rerender.
Outer is a wrapper around inner. It has no meaning in the scope of this question and is only there to give props to Inner and to simulate prop changes.
To demonstrate the desired functionality I have implemented Inner with a class based component. A live version of this code can be found on codesandbox. How can I migrate it to a functional component:
Inner.tsx:
import React, { Component } from 'react'
interface InnerProps{outerNum:number}
interface InnerState{innerNum:number}
export default class Inner extends Component<InnerProps, InnerState> {
state = {innerNum:0};
shouldComponentUpdate(nextProps:InnerProps, nextState:InnerState){
return this.props != nextProps;
}
render() {
return (
<button onClick={()=>{
this.setState({innerNum: Math.floor(Math.random()*10)})
}}>
{`${this.props.outerNum}, ${this.state.innerNum}`}
</button>
)
}
}
Outer.tsx:
import React, { useState } from "react";
import Inner from "./Inner";
export default function Outer() {
const [outerState, setOuterState] = useState(1);
return (
<>
<button
onClick={() => {
setOuterState(Math.floor(Math.random() * 10));
}}
>
change outer state
</button>
<Inner outerNum={outerState}></Inner>
</>
);
}
The official docs say to wrap the component in React.memo. But this doesn't seem to work for preventing rerenders on state change. It only applies to prop changes.
I have tried to make React.memo work. You can see a version of the code with both Outer and Inner being functional components here.
Related questions:
How to use shouldComponentUpdate with React Hooks? : This question only deals with prop changes. The accepted answer advises to use React.memo
shouldComponentUpdate in function components : This question predates stateful functional components. The accepted answer explains how functional components don't need shouldComponentUpdate since they are stateless.
React memo do not stop state changes
React.memo only checks for prop changes. If your function component
wrapped in React.memo has a useState or useContext Hook in its
implementation, it will still rerender when state or context change.
Ref:- https://reactjs.org/docs/react-api.html#reactmemo
Your Inner component depends on the property num of the Outer component, you can't prevent it from rendering on property change as React.memo makes properties comparison:
// The default behaviour is shallow comparison between previous and current render properties.
const areEqual = (a, b) => a.num === b.num;
export default React.memo(Inner, areEqual);
By memoizing the Inner component and removing the num dependency, it won't render on Outer rendering, see sandbox attached.
export default function Outer() {
const [outerState, setOuterState] = useState(1);
return (
<>
...
// v Inner is memoized and won't render on `outerState` change.
<Inner />
</>
);
}
If you want to implement shouldComponentUpdate with hooks you can try:
const [currState] = useState();
// shouldUpdateState your's custom function to compare and decide if update state needed
setState(prevState => {
if(shouldUpdateState(prevState,currState)) {
return currState;
}
return prevState;
});
React is by design driven by setState -> re-render loop. Props change is in fact a setState somewhere in parent components. If you don't want the setState to trigger a re-render, then why in the first place use it?
You can pull in a const state = useRef({}).current to store your internal state instead.
function InnerFunc(props) {
const state = useRef({ innerNum: 0 }).current;
return (
<button
onClick={() => {
state.innerNum = Math.floor(Math.random() * 10);
}}
>
{`${props.outerNum}, ${state.innerNum}`}
</button>
);
}
That said, it's still a valid question to ask: "how to implement shouldComponentUpdate in a react hook fashion?" Here's the solution:
function shouldComponentUpdate(elements, predicate, deps) {
const store = useRef({ deps: [], elements }).current
const shouldUpdate = predicate(store.deps)
if (shouldUpdate) {
store.elements = elements
}
store.deps = deps
return store.elements
}
// Usage:
function InnerFunc(props) {
const [state, setState] = useState({ innerNum: 0 })
const elements = (
<button
onClick={() => {
setState({ innerNum: Math.floor(Math.random() * 10) });
}}
>
{`${props.outerNum}, ${state.innerNum}`}
</button>
);
return shouldComponentUpdate(elements, (prevDeps) => {
return prevDeps[0] !== props
}, [props, state])
}
Noted that it's impossible to prevent a re-render cycle when setState is called, the above hook merely makes sure the re-rendered result stays the same as prev rendered result.
you should use the event that provide the browser and capture in the function before setState, like this
function setState = (e) =>{ //the e is the event that give you the browser
//changing the state
e.preventDefault();
}
After reading here about immutability, I am trying to understand how react is working. I am trying to understand with the help of following 3 components, however its not making sense.
const bla = {a: 1};
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
a: 1,
};
this.onClick = () => this.setState(prevState => ({
a: prevState.a + 1
}));
}
render() {
console.log('Render default');
return <div id="test" onClick = {
this.onClick
} > DOM Updating: {
this.state.a
} < /div>;
}
}
class Test1 extends React.Component {
constructor(props) {
super(props);
this.state = {
a: 1,
};
this.onClick = () => this.setState(prevState => ({
a: 1
}));
}
render() {
console.log('Render 1');
return <div id="test1" onClick = {
this.onClick
} > DOM not updating: {
this.state.a
} < /div>;
}
}
class Test2 extends React.Component {
constructor(props) {
super(props);
this.state = bla;
this.onClick = () => {
const mutating = this.state;
mutating.a = this.state.a + 1;
this.setState(mutating);
};
}
render() {
console.log('Render 2');
return <div id="test2" onClick = {
this.onClick
} > DOM updating with mutation: {
this.state.a
} < /div>;
}
}
ReactDOM.render( < div > < Test / > < Test1 / > < Test2 / > < /div>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
If I inspect div id="test1" it is updating in Chrome dev tool on each click. Want to understand below points:
During on click If I inspect div id="text1" why this is not reflecting in dev tool? Here I am assigning new object in state on each click ({a: 1}). As the link above says
In fact, React.js does not need to have knowledge about what exactly changed. All it needs to know is whether the state changed at all or not.
In Test2 component I am mutating the state however, why dom getting updated on click?
In case of updating nested state since we are updating parent state, does this mean React will think that all others children have changed their values and will re-render all of them even when they aren't changed?
1.) In the console output, you simply see Render 1, because you invoke setState with the static value 1. But in fact, each time you invoke setState with the click, React will set a new (in the sence of new object reference) state for the component, and a new render cycle is triggered.
setState() will always lead to a re-render unless shouldComponentUpdate() returns false. Link
2.) See answer 1. In addition, you have a DOM change, because state changes are merged.
When you call setState(), React merges the object you provide into the current state.
In your Test2 click handler code, mutating will be merged into the current state. It is the same object, but important is, that all its properties (here a property) will be spread into the state, resulting in a new state object.
const mutating = this.state;
mutating.a = this.state.a + 1;
this.setState(mutating);
3.) Let's say you have a Parent component containing all your child components. If you change props or state of Parent, all children will be rerendered by React, unless their shouldComponentUpdate hook returns false or they are PureComponents, whose props or state did not change.
setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state. Link
Rerender means, that for each child, React compares the previous and the current version in a Reconciliation step. It only leads to a DOM update, if the child has really changed, otherwise nothing happens. See here for a nice visualization.
I have some heavy forms that I'm dealing with. Thus, I'm trying to squeeze performance wherever I can find it. Recently I added the Why-did-you-render addon to get more insight on what might be slowing down my pages. I noticed that, for example, when I click on a checkbox component about all of my other components re-render. The justification is always the same. WDYR says
Re-rendered because of props changes: different functions with the
same name {prev onChangeHandler: ƒ} "!==" {next onChangeHandler: ƒ}
As much as possible, I try to respect best the best practices indications that I find. The callback functions that my component passes follow this pattern
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
export function TopLevelComponent({props}){
const defaultData = {name: '', useMale: false, useFemale: false}
const [data, setData] = useState(defData);
const { t } = useTranslation();
const updateState = (_attr, _val) => {
const update = {};
update[_attr] = _val;
setData({ ...data, ...update });
}
const updateName = (_v) => updateState('name', _v);//Text input
const updateUseMale = (_v) => updateState('useMale', _v);//checkbox
const updateUseFemale = (_v) => updateState('useFemale', _v);//checkbox
...
return <div>
...
<SomeInputComponent value={data.name} text={t('fullName')} onChangeHandler={updateName} />
<SomeCheckboxComponent value={data.useMale} onChangeHandler={updateUseMale} text={t('useMale')}/>
<SomeCheckboxComponent value={data.useFemale} onChangeHandler={updateUseFemale} text={t('useFemale')}/>
...
</div>
}
In an example like this one, altering any of the inputs (eg: Writing text in the text input or clicking one of the checkboxes) would cause the other 2 components to re-render with the justification presented above.
I guess that I could stop using functional components and utilize the shouldComponentUpdate() function, but functional components do present some advantages that I'd rather keep. How should I write my functions in such a way that interacting with one input does not force an update on another input?
The problem stems from the way you define your change handlers:
const updateName = (_v) => updateState('name', _v)
This line is called on each render and thus, every time your component is rendered, the prop has a new (albeit functionality-wise identical) value. The same holds for every other handler as well.
As an easy solution you can either upgrade your functional component to a fully fledged component and cache the handlers outside of the render function, or you can implement shouldComponentUpdate() in your child components.
You need to use memo for your child components to reduce renders
const SomeInputComponent = props => {
};
export default memo(SomeInputComponent);
// if it still causes rerender witout any prop change then you can use callback to allow or block render
e.f.
function arePropsEqual(prevProps, nextProps) {
return prevProps.name === nextProps.name; // use your logic to determine if props are same or not
}
export default memo(SomeInputComponent, arePropsEqual);
/* One reason for re-render is that `onChange` callback passed to child components is new on each parent render which causes child components to re-render even if you use `momo` because function is updated on each render so in order to fix this, you can use React hook `useCallback` to get the same function reference on each render.
So in you parent component, you need to do something like
*/
import { useCallback } from 'react';
const updateName = useCallback((_v) => updateState('name', _v), [])
You have to memoize parent function before pass to children, using useCallback for functional component or converting to class property if you use class.
export default class Parent extends React.PureComponent {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
console.log("click");
}
render() {
return (
<ChildComponent
onClick={ this.onClick }
/>
);
}
}
with useCallback:
Parent = () => {
const onClick = useCallback(
() => console.log('click'),
[]
);
return (
<ChildComponent
onClick={onClick}
/>
);
}
Trying to create a delay on react component that has input field that updates on change
Here is my onChange method
handleOrderQtyKeyPress (e) {
var regex = /[^0-9]/
if (e.key.match(regex)) {
e.preventDefault();
}
if (this.state.orderQtyValue.toString().length == 3) {
e.preventDefault();
}
}
and the react-bootstrap component:
<FormControl
type='number'
min='0'
value={this.state.orderQtyValue}
onChange={this.handleOrderQtyChange}
onKeyPress={this.handleOrderQtyKeyPress}
style={styles.orderQtyValue}
/>
so I tried importing lodash _.debounce and applying at the constructor
import debounce from 'lodash/debounce';
this.handleOrderQtyKeyPress = _.debounce(this.handleOrderQtyKeyPress.bind(this),1000);
I am not getting a debounce. What am I missing here?
I see that you use this, so I assume that FormControl is inside of a render function of your stateful component. In this case make sure that your binding and debouncing is happening in constructor of this stateful component.
```
const Component extends React.Component {
constructor(props) {
super(props);
this.handleOrderQtyKeyPress = _.debounce(this.handleOrderQtyKeyPress.bind(this), 1000);
}
}
```
Please, read comment which explains how this works
class Input extends Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
value: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
]),
}
state = {
value: '',
}
// When component receives updated `value` from outside, update internal `value` state.
componentWillReceiveProps(nextProps) {
this.setState({ value: nextProps.value });
}
// Store updated value localy.
onChange = (event) => {
this.setState({ value: event.target.value });
}
onBlur = (event) => {
// Trigger change to external env.
this.props.onChange(this.state.value);
// Also, don't forget to call `onBlur` if received from parent.
if (this.props.onBlur) {
this.props.onBlur(event);
}
}
render() {
return <input {...this.props} value={this.state.value} onChange={this.onChange} onBlur={this.onBlur} />
}
}
If you want to automatically debounce (or throttle) a component easily, when the props change often (as opposed to internal state changed),
I've authored a tiny wrapper (1kb minified) for that, called React-Bouncer:
import bouncer from '#yaireo/react-bouncer'
// uses 300ms `debounce` by default
const DebouncedYourComponent = bouncer(YourComponent)
This is useful when you do not have much of a control on the rate which the props sent to the component are updated, or the root cause of the often updates is unknown.
(Obviously, using debounce on the root-cause is the first thing to try)