React how to focus div when contentEditable is set to true? - javascript

I've got some react code like the following (minimal reproducible example):
import React, { useState } from 'react';
const Todo = ({todo}) => {
const [isUpdating, setisUpdating] = useState(false)
const updateItemHandler = (e) => {
setisUpdating(true);
}
return(
<div onClick={updateItemHandler} className={fa ${isUpdating ? "fa-check" : "fa-pencil"}`}></div>
<div id={todo.id}>
<div suppressContentEditableWarning={true}
contentEditable = {isUpdating}>{todo.value}
</div>
</div>
)
}
export default Todo;
When I clicked the div, it does change contentEditable to true, but I would also like to focus the newly editable div at the same time. I tried modifying my updateItemHandler function like so:
const updateItemHandler = (e) => {
setisUpdating(true);
e.focus();
}
but React threw an error/said focus wasn't a function here.
Is there some way I can automatically focus the div when I change the contentEditable to true?
Thanks

You can try something like this
import React, { useState, useRef } from 'react';
const Todo = ({todo}) => {
const ref = useRef(null)
const [isUpdating, setisUpdating] = useState(false)
const updateItemHandler = (e) => {
setisUpdating(true);
ref.current.focus()
}
return(
<div className="row text-center" id={todo.id}>
<div suppressContentEditableWarning={true}
contentEditable = {isUpdating} ref={ref}>{todo.value}
</div>
</div>
)
}
export default Todo;
Using ref (reference) may help you

Related

Chane child state from parent in React

I just started learning React and I have a couple cards I want to toggle a className from the component and remove the className from Parent. How do I do this? This is the child component
import React from 'react'
import { useState} from 'react'
const Card = ({check,cardID}) => {
const [isToggled,setToggle] = useState(false)
const flip = () => {
setToggle(!isToggled)
check()
}
return (
<div className='flip-card' >
<div id={`card${cardID}`} onClick={flip} className={`flip-card-inner ${isToggled ? "flipclass":""}`}>
<div className="flip-card-front">
<span>Flip for surprise</span>
</div>
<div className="flip-card-back">
<img className="card-img" src="/firstsect.jpg" alt="Avatar" height="100%" width="100%" />
</div>
</div>
</div>
)}
export default Card
and in the parent component I want to set the isToggled state to false so the flipclass wont be added
import React from 'react'
import Card from './Card'
import { useState, useRef } from 'react'
export const Cards = ({cardsarray,cardID}) => {
const [Counter,setCounter] = useState(0)
const check = ()=> {
setCounter(count => count+ 1)
if (Counter === 1) {
//Change isToggled on the other component to false
setCounter(0)
}
}
return (
<div className="all-cards">
{
cardsarray.map((_user,index)=>(
<Card cardID={index} check={check} key={index}></Card>
))
}
</div>
)}
I already tried using the state in the parent component and passing the istoggled as props but the whole array of cards gets the class instead of the particular card that calls the function. How do i go about this?

Adding Transition Effect to JSX Component in React

I would like to add a transition effect when the button is clicked in the following React component but as this code changes the actual JSX content rather than the className I don't think this can be done in CSS. How can I add a transition effect in this case?
import React, { useState } from 'react';
import ChatItem from './ChatItem';
import ChatButton from './assets/ChatButton';
import classes from './ChatBot.module.css';
const ChatList = (props) => {
const [selected, setSelected] = useState(props.items[0]);
function handleChangeSelected(item) {
setSelected(item);
}
const questionButtons = props.items.map((item) => {
if (item.id === selected.id) return null;
return (
<ChatButton onClick={handleChangeSelected.bind(null, item)}>
{item.question}
</ChatButton>
);
});
return (
<div className={classes.faq}>
<section className="textbox">
<ChatItem
key={selected.id}
id={selected.id}
question={selected.question}
answer={selected.answer}
/>
</section>
<div className={classes.questions}>{questionButtons}</div>
</div>
);
};
export default ChatList;
It is possible with some sort of library like react transition group. But I think it might be easier to apply a className for this if it's not a complex animation. Then apply your transition on the button. If in the way you could use visibility.
<ChatButton className={item.id === selected.id && 'active'} onClick={handleChangeSelected.bind(null, item)}>
{item.question}
</ChatButton>

Triggering a function in a React parent functional component from its child

Seen similar issues here, but couldn't wrap my mind on how this works. New to functional components and React overall.
Parent contains the Child, which is a modal. Parent has a div that triggers showing the Child modal, and the Child modal has a close button that triggers its hiding. When I click on the div component in Parent, I need to show and hide the Child modal. When I click on the close button in the Child, I need to hide the Child component.
The Parent component:
import React, { useState } from "react";
import Child from "./Child";
const Parent = () => {
const [buttonState, setbuttonState] = useState({
buttonState: false,
});
const onParentClick = (e) => {
e.preventDefault();
setbuttonState(!buttonState);
};
return (
<div>
<div onClick={onParentClick}></div>
<Child isOpen={buttonState} onParentClick={onParentClick} />
</div>
);
};
export default Parent;
The Child component:
import React, { useState } from "react";
const Child = (props) => {
const [buttonState, setButtonState] = useState({
buttonState: props.isOpen,
});
const onChildClick = (e) => {
e.preventDefault();
setButtonState(false);
props.onParentClick();
};
return (
<div
className={
buttonState ? "child-modal-opened" : "child-modal-closed"
}
>
<div onClick={onChildClick}>Close</div>
</div>
);
};
export default Child;
For some reason, can't make this work. What am I missing here?
Looks like useState() is used incorrectly.
const [buttonState, setbuttonState] = useState({
buttonState: false,
});
results in buttonState being { buttonState: false}, so setbuttonState(!buttonState) does not work as intended.
Here's updated Parent component with useState(false) instead (setting initial buttonState value to false)
import React, { useState } from "react";
import Child from "./Child";
const Parent = () => {
const [buttonState, setbuttonState] = useState(false);
const onParentClick = (e) => {
e.preventDefault();
setbuttonState(!buttonState);
};
return (
<div>
<div onClick={onParentClick}></div>
<Child isOpen={buttonState} onParentClick={onParentClick} />
</div>
);
};
export default Parent;
P.S.
As #Will suggested, there is no need to create another state in Child, it can be passed from Parent
import React, { useState } from "react";
const Child = (props) => {
return (
<div
className={
props.isOpen ? "child-modal-opened" : "child-modal-closed"
}
>
<div onClick={props.onParentClick}>Close</div>
</div>
);
};
It looks like onParentClick is defined so as to take an event object as a parameter and call preventDefault() on that, but you're calling it without any arguments. Does it work like this: props.onParentClick(e);?

Fix TypeError: matchesSelector is not a function in React component

I'm trying to use masonry-layout from here, to create a masonry grid in my component. However, I am running into the issue that clicking an element rendered by the component is returning the error TypeError: matchesSelector is not a function. I moved the masonry functionality to the component and wrapped it in a useEffect, which seemed to fix other rendering issue, but now it says that it can't trigger the functionality on click.
I was thinking I may need to use useRef on the grid, but not sure of the implementation here.
I'm able to get most of the functionality I want from working with it in plain JS in this fiddle, but trying to translate it to a React component.
import React, { useEffect } from 'react';
import Masonry from 'masonry-layout';
const Grid = items => {
useEffect(() => {
const grid = document.querySelector('.grid');
const masonry = new Masonry(grid);
let previouslyEnlarged;
let matchesSelector = () => {};
grid.addEventListener('click', function(event) {
if (!matchesSelector(event.target, '.grid-item')) {
return;
}
if (previouslyEnlarged === event.target) {
return;
} else if (previouslyEnlarged) {
previouslyEnlarged.classList.remove('grid-item--gigante');
}
previouslyEnlarged = event.target;
event.target.classList.toggle('grid-item--gigante');
masonry.layout();
});
});
return (
<div>
<div class="grid">
<div class="grid-holder">{items.map((item, i) => <div class="grid-item">{item.name}</div>)}</div>
</div>
</div>
);
};
export default Grid;
So the trick to enable access to each grid item and click it was to use a hook to set the state and toggle a class:
const [selected, setSelected] = useState(null);
const expandItem = e => {
const index = parseInt(e.target.getAttribute('data-index'));
setSelected(index);
};
<div className={`grid-item ${selected === i && 'grid-item--gigante'}`}
onClick={expandItem}
data-index={i}>item data</div>
let matchesSelector; is undefined. if you want it to be a function you have to initialize it. something like:
let matchesSelector = () => { ... };

How to create ref in class based component and pass it to the functional component in react?

So i have this class based component:
import React, { Component } from 'react'
import './OptionsMenu.sass'
import DropdownBox from '../DropdownBox/DropdownBox'
import Icon from '../Icon/Icon'
class OptionsMenu extends Component {
constructor(){
super()
this.dropdownBoxRef = React.createRef()
}
handleClickOutside = event => {
if (this.dropdownBoxRef && !this.dropdownBoxRef.current.contains(event.target)) {
this.props.close()
}
}
componentDidMount() {
document.addEventListener('mousedown', this.handleClickOutside)
}
componentWillUnmount() {
document.removeEventListener('mousedown', this.handleClickOutside)
}
render() {
const options = this.props.options.map(option => (
<li className='OptionsList-Element'>
<div className='OptionsList-ElementIcon'>
<Icon name={option.icon} />
</div>
<span>{option.label}</span>
</li>
))
return (
<DropdownBox reference={this.dropdownBoxRef} styles={this.props.styles}>
<ul className='OptionsList'>{options}</ul>
</DropdownBox>
)
}
}
export default OptionsMenu
In constructor i'm creating ref, and then i want to pass it to DropdownBox component, that is rendered. In DropdownBox component i tried to use react hooks, but i think that i'ts wrong way. How to make it correctly?
Note, i dont want to switch my functional component to classbased!
Here is the code of DropdownBox component:
const dropdownBox = props => {
const dropdownBoxRef = useRef(props.reference)
return (
<div ref={dropdownBoxRef} className='DropdownBox-Container' style={props.styles}>
<div className='DropdownBox'>
<div className='DropdownBox-Triangle' />
{props.children}
</div>
</div>
)
}
You can use forwarding refs to get a ref to the underlying element outside the child component. For example:
const DropdownBox = React.forwardRef((props, ref) => (
<div ref={ref} className='DropdownBox-Container' style={props.styles}>
<div className='DropdownBox'>
<div className='DropdownBox-Triangle' />
{props.children}
</div>
</div>
));
Then in OptionsMenu:
return (
<DropdownBox ref={this.dropdownBoxRef} styles={this.props.styles}>
<ul className='OptionsList'>{options}</ul>
</DropdownBox>
)
Just assign the reference prop in your DropdownBox component to the ref prop of the div you want the reference of.
const dropdownBox = props => {
return (
<div ref={props.reference} className='DropdownBox-Container' style={props.styles}>
{ /* ... */ }
</div>
)
}
React will assign the component to the dropdownRef variable on its own.
You can give a the dropdownBox a function that will set the ref. To do this you can add in your menu component a function like the following:
const onDropdownRef = (ref) => {
this.dropdownBoxRef.current = ref;
}
Then you give this function to your dropdown component like so: <DropdownBox onRef={this.onDropdownRef} styles={this.props.styles}>
Then finally in your dropdown component you give this function to the div like so:
<div ref={props.onRef} className='DropdownBox-Container' style={props.styles}>
This will make sure your menu will have a ref to the top div from the dropdownbox.

Categories