React Avoid Re-Render for Functional Component - reactjs

I am using Hooks and how can I avoid from re-render? My purpose of asking this quesiton , I am using scroll for overflow item from div height and when I focus on a item using onMouseOver, my funtional component is rendering itself and scroll is returning initial position. Therefore I dont delete last item of div. Could you any advice to avoid re-rendering ?
My component :
import React from 'react'
type removeDisplayItem = {
dataId: number;
display: boolean
}
const PlayerCalendar = () => {
const [removeDisplayData, setRemoveDisplayData] = useState<removeDisplayItem>({ dataId: -1, display: false })
const handleRemove = (dataId: number, display: boolean) => {
const removeData = {
dataId: dataId,
display: display
}
setRemoveDisplayData(removeData)
}
const renderScheduleContentCell = (data: scheduleItem) => {
return (
<>
<div className="cell-content-wrapper" style={removeDisplayData.dataId === data.id && removeDisplayData.display === true ? { display: "none" } : { display: "flex" }}
onMouseOver={() => handleDisplayRemove(data.id, true)}>
<div className="start-time-text">{moment(data.startTime).format("HH:mm")}</div>
<div className="dash"> - </div>
<div className="end-time-text"> {moment(data.endTime).format("HH:mm")} </div>
</div>
<div className="cell-content-wrapper" onMouseLeave={() => handleDisplayRemove(data.id, false)}
style={removeDisplayData.dataId === -1 ? { display: "none" } : removeDisplayData.dataId === data.id && removeDisplayData.display === true ? { display: "flex", justifyContent: "space-between", backgroundColor: "#8c9296", color: "white", cursor: "pointer" } : { display: "none" }}
onClick={() => setCalendarRemoveItemConfirm(true)}>
<div className="dash"> </div>
<div className="start-time-text">Remove</div>
<div className="end-time-text"><i className="fas fa-trash-alt"></i></div>
</div>
</>
)
}
return (
{renderScheduleContentCell(exampleData)}
)
}
export default PlayerCalendar;

Try to use the memo HOC and wrap it around PlayerCalendar component.
React.memo can be use to optimize your component. When you have the same props / state it can be used to memoize the data.

Related

React Select with avatar class component whats wrong here select not populating

Hi I had working select box with avatar on functional aporx. But I need build in functions to change selectbox properties like hidden: true/false, update option data function to add new option data to display selectbox with certain values on fly.
What I wrong here? Render part works as tested with functional version. The class factoring misses something as select box does not get info ether options and avatar to display and no width calculation happening.
Orginal functional code: https://codesandbox.io/s/react-select-longest-option-width-geunu?file=/src/App.js
Class based works but nodatin selectbox. Here is app.js with select.js fiddle: https://codesandbox.io/s/react-select-longest-option-width-forked-plqq0p?file=/src/Select.js
Source:
import React, { useRef } from "react";
import Select from "react-select";
class RtSelect extends React.Component {
constructor(props) {
super();
this.state = {
info: props.info,
options: props.options,
hidden: props.hidden,
menuIsOpen: false,
menuWidth: "",
IsCalculatingWidth: false
};
this.selectRef = React.createRef();
this.onMenuOpen = this.onMenuOpen.bind(this);
}
componentDidMount() {
if (!this.menuWidth && !this.isCalculatingWidth) {
setTimeout(() => {
this.setState({IsCalculatingWidth: true});
// setIsOpen doesn't trigger onOpenMenu, so calling internal method
this.selectRef.current.select.openMenu();
this.setState({MenuIsOpen: true});
}, 1);
}
}
onMenuOpen() {
if (!this.menuWidth && this.isCalculatingWidth) {
setTimeout(() => {
const width = this.selectRef.current.select.menuListRef.getBoundingClientRect()
.width;
this.setState({menuWidth: width});
this.setState({IsCalculatingWidth: false});
// setting isMenuOpen to undefined and closing menu
this.selectRef.current.select.onMenuClose();
this.setState({MenuIsOpen: true});
}, 1);
}
}
styles = {
menu: (css) => ({
...css,
width: "auto",
...(this.isCalculatingWidth && { height: 0, visibility: "hidden" })
}),
control: (css) => ({ ...css, display: "inline-flex " }),
valueContainer: (css) => ({
...css,
...(this.menuWidth && { width: this.menuWidth })
})
};
setData (props) {
this.setState({
info: props.info,
options: props.options,
hidden: props.hidden
})
}
render() {
return (
<div style={{ display: "flex" }}>
<div style={{ margin: "8px" }}>{this.info}</div>
<div>
<Select
ref={this.selectRef}
onMenuOpen={this.onMenuOpen}
options={this.options}
menuIsOpen={this.menuIsOpen}
styles={this.styles}
isDisabled={this.hidden}
formatOptionLabel={(options) => (
<div className="select-option" style={{ display: "flex" }}>
<div style={{ display: "inline", verticalAlign: "center" }}>
<img src={options.avatar} width="30px" alt="Avatar" />
</div>
<div style={{ display: "inline", marginLeft: "10px" }}>
<span>{options.label}</span>
</div>
</div>
)}
/>
</div>
</div>
);
}
}
export default RtSelect;
Got it working!
I had removed "undefined" from onOpen setState function. I compared those 2 fiddles and finally got it working.
// setting isMenuOpen to undefined and closing menu
this.selectRef.current.select.onMenuClose();
this.setState({MenuIsOpen: undefined});

Change background color of div by clicking in react

I have three div in the component(which can be more than three as well). I want to change their color when they will be clicked. If again I will click, they will get back their old color. In my code if I am clicking any one div, all div s are changing, Can you help me to do it for particular div?
The code is:
import React,{useState} from 'react'
export default function ChangeColor() {
let [colorState,changeState]=useState(['red','green','blue']);
let [isActive,setIsActive]=useState(true);
return (
<>
{colorState.map((color,index)=>{
return(
<React.Fragment key={index}>
<div style={{width:'100px',height:'100px',backgroundColor:isActive?`${color}`:'yellow' }}
onClick={()=>{isActive?setIsActive(false) :setIsActive(true)}}>
<p>{color}</p>
</div>
</React.Fragment>
)})
}
</>
)
}
All 3 div are changing together because only a single value is used to control the state.
To solve this, you could make isActive an object that contain a Boolean value for each color, so its structure could be something like this:
{red: true, green: false, blue: false}
This way, each of the div can set the styles based on condition like:
backgroundColor: isActive[color] ? 'yellow' : color
Full example: (live demo on stackblitz)
import React, { useState } from 'react';
export default function ChangeColor() {
const [colorState, changeState] = useState(['red', 'green', 'blue']);
const [isActive, setIsActive] = useState({});
const toggleActive = (color) =>
setIsActive((prev) => {
if (prev[color]) return { ...prev, [color]: false };
return { ...prev, [color]: true };
});
return (
<>
{colorState.map((color, index) => {
return (
<React.Fragment key={index}>
<div
style={{
width: '100px',
height: '100px',
backgroundColor: isActive[color] ? 'yellow' : color,
cursor: 'pointer',
}}
onClick={() => toggleActive(color)}
>
<p>{isActive[color] ? 'yellow' : color}</p>
</div>
</React.Fragment>
);
})}
</>
);
}

How can I show the div when its display none?

when i click the Cross (X) btn in div its should we close and when I click the Rundown List its show me div example:-
when I click the Business news Cross btn its close the div but when i click the rundown List business news its show me business new
i have try but i when i click the cross btn is again not open the div
Code:-.
Parent component
const LeftNav = () => {
return (
<div className="allDivs">
{item.map((items, index) => {
// console.log(item)
return (
<div key={index} >
<TabHeader item={items} index={index}/>
</div>
)
})}
</div>
</div >
)
}
export default LeftNav;
Child componentL;-
export default function TabHeader({ item, index }) {
const [Close, setClose] = useState(false);
useEffect(() => {
console.log("activeFOCUS", activeFOCUS);
setShow(ontoggle);
}, [])
return (
<Fragment>
<div id="CLOSEDIV" style={Close === true ? { display: "none" } : { display: "block" }}>
<div className="TableText" onClick={(e) => { handleOnClick(e, Delete.val) }}>
<div id="SHOW">{Delete.val}</div>
</div>
//cross btn
<div className="CloseIcon" id="CloseBtn"><FaRegTimesCircle
style={{ color: "#FC0000", width: "20px", height: "20px", alignItems: "right" }}
onClick={(e) => { handleToggle(e, index, Delete.val) }} /></div>
</div>
</div>
</Fragment >
)
}
please help....
Try using visibility: hidden and visibility: visible instead of display: none

React Hooks useRef initialization issue, useRef only works on subsequent calls

I am implementing useRef into my project. I have a form that has clickable sections. Once clicked it opens the form. I'm using Reactstrap Collapse to show/hide the form. I need to be able to open the form and show the section that needs to be filled out, however the scrollIntoView once I click the section doesn't work until I open and close the form again. I'm stumped. I console.log(formRef), the ref returns as expected of the component that I want to be scrolled to the top of viewport on subsequent calls. My guess would be that the formRef is being initialized as null to begin with so initial calls to the ref do not work. However, once it knows the ref the subsequent calls work. I'm not sure how to go about this..
If I need to provide an example that is stripped please let me know. I am expecting this to be just an initialization issue.
Form
import React, { useRef, useContext, useEffect } from "react";
import {
FormQuestionsContext,
FormAnswersContext,
ExpandedSectionContext,
} from "../../Store";
import SectionHeader from "../SectionHeader";
import ImageUploader from "../CommentsSection";
import Ratings from "../Ratings";
import { Collapse, Button, CardBody, Card } from "reactstrap";
import FontAwesome from "react-fontawesome";
import styles from "./bedthreeform.module.css";
function BedThreeForm({ Name }) {
const formRef = useRef(null); //useRef Initialization
const [expandedSection, setExpandedSection] = useContext(
ExpandedSectionContext
);
const [formQuestions, setFormQuestions] = useContext(FormQuestionsContext);
const [formAnswers, setFormAnswers] = useContext(FormAnswersContext);
const array = formQuestions.bedthree;
const onChange = (e, name) => {
const { value } = e.target;
setFormAnswers((state) => ({
...state,
[Name]: { ...state[Name], [name]: value },
}));
};
//! The function I use when I want to tell useRef to scrollIntoView
const handleOpen = () => {
expandedSection === Name
? setExpandedSection("")
: setExpandedSection(Name);
formRef.current.scrollIntoView();
};
const answeredQuestions = formAnswers.bedthree
? Object.keys(formAnswers.bedthree)
: null;
console.log(formRef);
return (
<div>
<Button
className={styles["CollapseBtn"]}
onClick={handleOpen} //Calling the function here
style={
answeredQuestions &&
answeredQuestions.length === formQuestions.bedthree.length
? {
color: "white",
":focus": {
backgroundColor: "#02BD43",
},
backgroundColor: "#02BD43",
marginBottom: "1rem",
width: "100%",
}
: answeredQuestions &&
answeredQuestions.length !== formQuestions.bedthree.length
? {
color: "white",
":focus": {
backgroundColor: "#bd0202",
},
backgroundColor: "#bd0202",
marginBottom: "1rem",
width: "100%",
}
: {
":focus": {
backgroundColor: "#fafafa",
},
marginBottom: "1rem",
width: "100%",
}
}
>
<p>BEDROOM #3 INSPECTION</p>
<FontAwesome
className="super-crazy-colors"
name="angle-up"
rotate={expandedSection === Name ? null : 180}
size="lg"
style={{
marginTop: "5px",
textShadow: "0 1px 0 rgba(0, 0, 0, 0.1)",
}}
/>
</Button>
<Collapse
className={styles["Collapse"]}
isOpen={expandedSection === Name}
>
<Card>
<CardBody>
{array ? (
<div>
<SectionHeader title="Bedroom #3 Inspection" name={Name} />
<div
ref={formRef}
className={styles["BedroomThreeFormWrapper"]}
id="bedroom-three-form"
>
{array.map((question, index) => {
const selected =
formAnswers[Name] && formAnswers[Name][question]
? formAnswers[Name][question]
: "";
return (
<div className={styles["CheckboxWrapper"]} key={index}>
<h5>{question}</h5>
<Ratings
section={Name}
question={question}
onChange={onChange}
selected={selected}
/>
</div>
);
})}
</div>
{!answeredQuestions ? (
""
) : (
<Button
onClick={(e) => e.preventDefault()}
style={
!answeredQuestions ||
(answeredQuestions &&
answeredQuestions.length !==
formQuestions.bedthree.length)
? {
backgroundColor: "#bd0202",
color: "white",
pointerEvents: "none",
}
: {
backgroundColor: "#02BD43",
color: "white",
pointerEvents: "none",
}
}
>
{!answeredQuestions ||
(answeredQuestions &&
answeredQuestions.length !==
formQuestions.bedthree.length)
? "Incomplete"
: "Complete"}
</Button>
)}
<br />
<ImageUploader name="bedthree" title={"Bedroom #3"} />
</div>
) : (
<div></div>
)}
</CardBody>
</Card>
</Collapse>
</div>
);
}
export default BedThreeForm;
CodeSandbox Stripped Form Doesn't work as expected, however that is the stripped code.
Update I'm open to suggestions to bypass this, or an alternative way to do this. I'm not sure why it only does it on subsequent calls.
Look at these lines:
<CardBody>
{array ? (
...
<div
ref={formRef}
...
This (virtual) dom will be evaluated only if array is defined. In case you would like to have your formRef always to point to the dom, then You'll have to strip it out from your condition.
I've figured out the issue, the issue is calling it when the content in the collapse hasn't been loaded yet, Reactstrap has an attribute onEntered which basically when set, will run the function as soon as the collapse has fully opened. The example that I found is here. Also, by setting the attribute innerRef on a Reactstrap component I can manipulate it just like I could a regular component using ref.

React state does not get updated

I know there's been many questions with this topic asked already, but it really feels like each and every one of them is different and I cannot find one that matches my issue closely enough.
I have a grid with draggable ItemComponents. Once selected, additional action icons show up (ItemActionIcon). I would very much like to unselect the component (and effectively hide the action icons) once one of the actions is clicked.
and so in line 77 <div onClick={() => this.setState({selected: false})} key={index}> I'm attempting to update the state of selected to false. It already works just fine in all other cases mentioned in the file. But not in this case. When I click the icon, I can see with a debugger (or with a console.log when I tried it) that the onClick action is triggered as expected and the ItemComponent even gets another call to the render method, but the this.state.selected is still set to true.
import React, {Component} from "react";
import Draggable, {DraggableBounds} from "react-draggable";
import ItemComponentAction from "./ItemComponentAction";
import ItemActionIcon from "./ItemActionIcon";
export interface Position {
x: number;
y: number;
}
export interface ItemComponentProps {
gridSquareSize: number;
canvasBounds: DraggableBounds;
margin: number;
position: Position;
}
interface ItemComponentState {
gridSquareSize: number;
canvasBounds: DraggableBounds;
margin: number;
selected: boolean;
}
export default abstract class ItemComponent extends Component<ItemComponentProps> {
protected abstract readonly icon: string;
protected abstract readonly actions: ItemComponentAction[];
state: ItemComponentState;
protected constructor(props: ItemComponentProps) {
super(props);
this.state = {
gridSquareSize: props.gridSquareSize,
canvasBounds: props.canvasBounds,
margin: props.margin,
selected: false
};
}
render() {
return (
<Draggable grid={[this.state.gridSquareSize / 2, this.state.gridSquareSize / 2]}
defaultPosition={{
x: this.state.margin + this.props.position.x * this.state.gridSquareSize,
y: this.state.margin + this.props.position.y * this.state.gridSquareSize
}}
handle=".handle"
bounds={this.state.canvasBounds}
onDrag={() => this.setState({selected: false})}
>
<div tabIndex={0}
className="handle"
onClick={() => this.setState({selected: true})}
onBlur={() => this.setState({selected: false})}
style={{
position: 'absolute',
backgroundColor: 'red',
width: this.state.gridSquareSize,
height: this.state.gridSquareSize,
cursor: "move"
}}
>
{this.icon}
{
!this.state.selected || !this.actions.length
? null
: (
<div style={{
position: 'absolute',
bottom: "0"
}}>
{
this.actions.map((action, index) => (
<div onClick={() => this.setState({selected: false})} key={index}>
<ItemActionIcon {...action}/>
</div>
))
}
</div>
)
}
</div>
</Draggable>
);
}
}
so what's the deal?
The outer <div> of your component has its own onClick handler which is setting the value of your state back to false. Try using stopPropagation() on the inner onClick handled event. That will prevent the event from propagating to the outer parent <div>, and only the inner onClick handler will execute when it is clicked on.
{
!this.state.selected || !this.actions.length ? null : (
<div
style={{
position: "absolute",
bottom: "0"
}}
>
{this.actions.map((action, index) => (
<div
onClick={e => {
e.stopPropagation();
this.setState({ selected: false });
}}
key={index}
>
<ItemActionIcon {...action} />
</div>
))}
</div>
);
}

Resources