(React) How to put focus on first element when component updates? - reactjs

I have the following code in react.
In ParentComponent, I render either first or second panel depending on whether certain conditions are met. Let's say FirstPanel is rendered.
Inside FirstPanel, I am able to do some interactions that would update the panel and I need the focus to go to the first element in the first panel (aka the first div element)
How can I do this? I tried using this.props.setRef.focus() but it doesn't work.
ParentComponent
setCurrentPanel(panel) {
this.currentPanel = panel;
}
render() {
if (this.state.showFirstPanel)
return <FirstPanel setRef={this.setCurrentPanel} />;
if (this.state.showSecondPanel)
return <SecondPanel setRef={this.setCurrentPanel} />;
return undefined;
}
FirstPanel
componentDidUpdate() {
// put focus on component FirstPanel again
}
render() {
return <div role="tabpanel" tabIndex="0" ref={this.props.setRef}>
{children}
</div>
}

Using useRef and useEffect you can do that way:
import React , {useRef, useEffect, useState} from 'react';
const MyComponent = () => {
const [valueThatWillChange, setValueWillChange] = useState(null);
const ref = useRef(null);
useEffect(() => {
ref.current.focus();
}, [valueThatWillChange]);
return (
<div>
<input type="text" ref={ref} />
<input type="text" onBlur={e => setValueThatWillChange(e.target.value)} />
</div>
);
};
Each time that the cursor out of second input he will back to the first one

Based on your ParentComponent implementation, when the state changes, FirstPanel will unmount, and then SecondPanel mounts.
When you switch again, SecondPanel unmounts, and FirstPanel mounts.
To achieve what you are after, simply do the focus manipulation in both componentDidUpdate and componentDidMount lifecycles.
In addition, if your ParentComponent is not going to anything with currentPanel, you can creatRef in each Panel component. Your setRef at the moment is simply an function, not a Ref object that contains reference to the dom element.

Related

can we able to specify on click on the component definition itself?

I want to create a component(comp) with onclick event handler. which should be handled by the component itself (i.e. inside the comp.js file).
if I use it inside the parent component we don't need to specify the event but it is handled by the component element(comp element).
is this possible. Any idea to develop this one.
in ParentComponent.js current behavior.
<NewComponent onClick={clickBehaviour}/>
I want like,
In NewComponent.js
const NewComponent.js = ()=>{
// Some code
const clickBehaviour = () =>{
// click behaviour
}
}
Is it possible in the current standards?
why you want to write your onClick event in parent component?
you can do it inside NewComponent.js easily.
just do this:
import React from 'react'
function NewComponent() {
const clickBehaviour = () =>{
// click behaviour
}
return (
<div onClick={clickBehaviour}>
//some jsx here
</div>
)
}
export default NewComponent
and use in anywhere you want to use without onClick event :
< NewComponent />
i cant understand well you situation but you can use forwardRef if you want (also can use old getElementById but using forwardRef is recommended).
import React, { useRef } from "react";
const NewComponent = React.forwardRef((props, ref) => (
<div onClick={() => alert("div 2 clicked")} ref={ref}>
div 2
</div>
));
export default function App() {
const compRef = useRef(null);
return (
<div>
<NewComponent ref={compRef} onClick={() => {
compRef && compRef.current && compRef.current.click();
}} />
</div>
);
}

Why does React re-render children when the parent changes?

If a parent re-renders, children in React are also re-rendered, no matter if the passed props changed or not.
Why is React doing that? What would be the issue if React wouldn't re-render children (without changed props) when the parent renders?
Update: I am talking about this in the React Devtools profiler:
Sample code:
App.tsx:
import React, { useMemo, useState } from "react";
import "./App.css";
import { Item, MyList } from "./MyList";
function App() {
console.log("render App (=render parent)");
const [val, setVal] = useState(true);
const initialList = useMemo(() => [{ id: 1, text: "hello world" }], []); // leads to "The parent component rendered"
//const initialList: Item[] = []; // leads to "Props changed: (initialList)"
return (
<div style={{ border: "10px solid red" }}>
<button type="button" onClick={() => setVal(!val)}>
re-render parent
</button>
<MyList initialList={initialList} />
</div>
);
}
export default App;
MyList.tsx:
import { FC, useState } from "react";
export interface Item {
id: number;
text: string;
}
interface Props {
initialList: Item[];
//onItemsChanged: (newItems: Item[]) => void;
}
export const MyList: FC<Props> = ({
initialList,
//onItemsChanged,
}) => {
console.log("render MyList");
const [items, setItems] = useState(initialList);
return (
<div style={{ border: "5px solid blue" }}>
<ul>
{items.map((item) => (
<li key={item.id}>{item.text}</li>
))}
</ul>
<button type="button">add list item (to be implemented)</button>
</div>
);
};
React achieves a fast and responsive UI by re-rendering components on every state change (using setState) or from changes of props, followed by React’s reconciliation diffing algorithm that diffs previous renders with current render output to determine if React should commit changes to the component tree (e.g. DOM) with the new updates.
However, unnecessary component re-renders will happen and can be expensive, It’s been a common performance pitfall in every single React project that I’ve been working on. SOURCE
Solution for this issue :
A component can re-render even if its props don’t change. More often than not this is due to a parent component re-rendering causing the child to re-render.
To avoid this, we can wrap the child component in React.memo() to ensure it only re-renders if props have changed:
function SubComponent({ text }) {
return (
<div>
SubComponent: { text }
</div>
);
}
const MemoizedSubComponent = React.memo(SubComponent);
SOURCE
Memoization generates an additional cost corresponding to cache-related computations, this is why React re-renders components even when the props are referentially the same, unless you choose to memoize things using React.memo for instance.
If you consider for example a component that re-renders with different props very often, and if memoization was an internal implementation detail, then React would have to do 2 jobs on every re-rendering:
Check if the old and current props are referentially the same.
Because props comparison almost always returns false, React would then perform the diff of previous and current render results.
which means that you might end up with worse performance.
Wrapp your component with React.memo and it will not re-render
export const MyList: FC<Props> = React.memo(({
initialList,
//onItemsChanged,
}) => {
console.log("render MyList");
const [items, setItems] = useState(initialList);
return (
<div style={{ border: "5px solid blue" }}>
<ul>
{items.map((item) => (
<li key={item.id}>{item.text}</li>
))}
</ul>
<button type="button">add list item (to be implemented)</button>
</div>
);
})
If you are looking at reason, please see this
Well, the component only re-renders if shouldComponentUpdate() returns true, which it does by default. It is usually not much of a problem because the DOM will still not update if there are no changes. As an optimization, you can still add logic to keep your child components from re-rendering if its state and props have not changed, as follows:
shouldComponentUpdate(nextProps, nextState) {
return (this.props.someProp !== nextProps.someProp && this.state.someState !== nextState.someState);
}
Do remember though this is just a performance optimization, when in doubt stick to the default behaviour. Here is a link to the official documentation.
Here is a little analogy that should help
Let's say you have a box, and a box within that box. If you want to replace the outer box with a new box, then you must also replace the box inside it.
Your parent component is like the outer box, and the child component like the inner box. If you clear away the first render to make room for a new render, that new render will re-render new child components inside it.
I hope this helps clear things up for you.
Edit:
If you were not to re-render the child component inside the parent then any props that might be passed to the child component would not update within it and therefore, the child would be forever without dynamic props, and the whole purpose of ReactJS would be lost. You wouldn't be here to ask this question in the first place anyway if that was the case.
When Parent Component render will not render child components,for using memo and useCallback.
Child Component:
Using memo will cause React to skip rendering a component if its props have not changed.
import { React, memo } from "react";
import { Typography, TextField } from "#mui/material";
function PasswordComponent(props) {
console.log("PasswordComponenmt");
return (
<div>
<Typography style={{ fontWeight: "900", fontSize: 16 }}>
Password
</Typography>
<TextField
size="small"
type="password"
variant="filled"
value={props.password}
onChange={(e) => {
props.onChangePassword(e.target.value);
}}
/>
</div>
);
}
export default memo(PasswordComponent);
Parent Component:
The React useCallback Hook returns a memoized callback function.
The useCallback Hook only runs when one of its dependencies update.
This can improve performance.
import { React, useState, useCallback } from "react";
export default function ParentComponent() {
const [password, setpassword] = useState("");
const onChangePassword = useCallback((value) => {
setpassword(value);
}, []);
return <PasswordComponent onChangePassword={onChangePassword} />;
}

How to create a component in a custom hook that handles state within the hook?

How can I create a component in a custom hook where the hook holds the state for the component?
My attempt basically does the right thing, but drag-and-drop is not working as expected. Instead the slider will only change the value on click. I think the problem is, that the useState hook gets called outside of the X definition. But how can we create a component in a hook then where I need to work with the state of that internal component within the rest of the hook?
https://codesandbox.io/s/material-demo-milu3?file=/demo.js:0-391
import React from "react";
import Slider from "#material-ui/core/Slider";
function useComp() {
const [value, setValue] = React.useState(30);
const X = () => <Slider value={value} onChange={(_, v) => setValue(v)} />;
return { X, value };
}
export default function ContinuousSlider() {
const { X, value } = useComp();
return (
<div>
{value}
<X />
</div>
);
}
Whenever the custom hook is called (on each render), a new Slider (Broken) component is created. Since a new component is created, the event handlers are recreated as well, and the drag is cancelled. You can solve this problem in two ways:
Wrap the component in useCallback(), and pass the value when rendering the component (sandbox):
const Broken = useCallback(({ value }) => (
<Slider value={value} onChange={changeHandler} />
), [changeHandler]);
// usage
<Broken value={broken} />
Render the component in the hook, and use the include it in the component (sandbox):
function useComp() {
const [broken, setBroken] = React.useState(30);
const changeHandler = useCallback((_, v) => setBroken(v), []);
const slider = <Slider value={broken} onChange={changeHandler} />;
return { slider, broken };
}
// usage
<div>
Broken: {broken}
{slider}
OK: {ok}
<Slider value={ok} onChange={(_, v) => setOk(v)} />
</div>

ReactJs: Prevent Rerender of wrapped component

I'm trying to prevent a re-render when using custom hook for hours now -.-, need some help ;O|
(Dont know if I should call this custom hook or functional hoc though)
I have a MessageList component that display a SimpleMessage wrapped in WithAvatarHeader.
Here is my profiler result:
Every time I add a message to the list, all messages are rendered again.
This isn't happening when I only use SimpleMessage in MessageList
Is there a way to memo(WithAvatarHeader) ?
MessageList :
import React from "react";
import SimpleMessage from "./SimpleMessage";
import WithAvatarHeader from "./WithAvatarHeader";
const MessageList = props => {
const Message = WithAvatarHeader(SimpleMessage);
return (
<div className="message-list">
{props.messages.map(message => {
return <Message message={message} key={message._id}/>;
})}
</div>
);
};
SimpleMessage:
import React, { memo } from "react";
const SimpleMessage = props => {
return (
<div className="simple-message">
{props.message}
</div>
);
};
export default memo(SimpleMessage);
WithAvatarHeader:
import React from "react";
const WithAvatarHeader = WrappedComponent => props => {
return <WrappedComponent {...props} />;
};
export default WithAvatarHeader;
Thanks for the help :-)
You should not declare component inside another component.
Once you move declaration outside:
const Message = WithAvatarHeader(SimpleMessage);
const MessageList = props => {
return (
<div className="message-list">
{props.messages.map(message => {
return <Message message={message} key={message._id}/>;
})}
</div>
);
};
you will be fine.
Reason is reconciliation process that decides what's to drop, what to create and what to update.
Besides your JSX says it still same element <Message> React checks component's constructor(it does not work with text representation from JSX). And it will referentially different(since you re-declare this constructor on next render). So React drops every <Message> and create them from scratch. Keeping declaration outside your MessageList means constructor is referentially the same so React will not re-create <Message> till key is the same.

How to store user input data in console log on form submit from child to parent component?

I have my child components within my parent component and I would like to be able to console log what data a user has submitted. However, being new to react I am not sure how I should do this?. Would love some help!
import React, { Component } from 'react';
import './App.css';
import PageOne from './Components/PageOne';
import PageTwo from './Components/PageTwo';
import PageThree from './Components/PageThree';
import PageFour from './Components/PageFour';
import PageFive from './Components/PageFive';
import PageSix from './Components/PageSix';
import PageSeven from './Components/PageSeven';
import { Input, Dropdown, TextArea, Form, Button, Header } from 'semantic-
ui-react'
class App extends Component {
render() {
return (
<div className="App">
<PageOne />
<PageTwo />
<PageThree />
<PageFour />
<PageFive />
<PageSix />
<Button>
Submit Form
</Button>
<br/>
<br/>
</div>
)
}
}
export default App;
We can pass a callback through the Parent component as props to Child and invoke it after handling the form submission.
You cant really "store" it in the console, What are you trying to do exactly ?
Do you want to save the user submitted data to use later in a part of your App? if so you could pass a call back to set the state in your Parent component with the data submitted, this way you can share the data with other Child components through the Parent state
The important thing to understand here is that we pass callbacks as props to Child components when we need to pass data to Parent components and save it to our state this way our Parent can share the state with the rest of our App.
function Parent() {
const logAfterSubmit = data => {
console.log("user submitted data", data);
};
return (
<div className="App">
<Child logAfterSubmit={logAfterSubmit} />
</div>
);
}
function Child({ logAfterSubmit }) {
const [input, setInput] = useState("");
const handleSubmit = e => {
e.preventDefault();
// do something with the data
// call our function from parent with the data
logAfterSubmit(input);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" onChange={e => setInput(e.target.value)} />
<button type="submit">Submit</button>
</form>
);
}
SandBox
Welcome to stackoverflow. React is an amazing tool for developing websites. Unique to React is a concept called "state" which is essentially what you're looking for. "State" is React's ability to keep track of data and use it accordingly. In your case, you want to record what a user submits in your child component.
Let's consider the following code:
class Parent extends React.Component{
//define state here which is typically an empty object
state = {
text: ""
}
//this is an event handler we have set up to take the data from state
handleOnSubmit = () => {
event.prevent.default() //stops page from refreshing
console.log(this.state.text)
}
//event handler that is triggered every time a user makes a change to the input
handleOnChange = (event) => {
//unique method in React that lets you update the component state
this.setState({
text: event.target.value
})
}
//all class-based components are called with render
render(){
return(
//we can pass down event handlers/functions we've defined into child components.
<Child handleOnChange={this.handleOnChange} handleOnSubmit={this.handleOnSubmit}/>
)
}
}
Now for our Child Component
//this is known as a stateless-functional component. no need to keep track of state.
const Child = (props) => {
return(
<form onSubmit={props.handleOnSubmit}>
//onChange is a unique-event listener that gets called anytime characters changes inside an input tag. So in this case we update our Parent's state every time we type.
<input onChange={props.handleOnChange} name="text" />
</form>
)
}
So a high-level summary of this. We set up a parent-component and defined functions inside of it that will help keep track of "state" or data. Then we pass down those functions as properties to our Child component. Our child component uses those functions and when they are called, they will update the state in our Parent component. Thus giving you a controlled-input flow, where you are constantly keeping track of data coming into your forms.

Resources