React tabIndex onBlur don't hide element - reactjs

I have a sidebar element that uses tabIndex and onBlur to control visibility, so when the user selects anything outside of the sidebar it automatically hides.
This works well, but I need to add a drop-down menu to the sidebar which then gets focus and causes the sidebar to collapse (before a user could select something).
state = {
visible: true
}
componentDidMount () {
this.focusSidebar()
}
componentDidUpdate () {
if (this.state.visible) this.focusSidebar()
}
focusSidebar () {
ReactDOM.findDOMNode(this.refs.sidebarRegion).focus()
}
hideSidebar () => {
this.setState({ visible: false })
}
render () {
return (
<div
onBlur={this.hideSidebar}
tabIndex='0'
className={`sidebar ${this.state.visible ? '' : 'hidden'}`}
ref='sidebarRegion'
>
<select>
<option>Foo</option>
</select>
</div>
)
}
I'm not seeing a good way to handle this with my current implementation of the sidebar, but I'm trying to find a way to self-contain the sidebar element without needing to hoist the visible/hidden state outside of the component.

You can use document.activeElement to achieve what you want. I will not add more details, as it was explained here. You can also take a look at this gist.
Here it is demonstrated with your code, I didn't add css, but a console log to tell you when it should hide:
class Hello extends React.Component {
state = {
visible: true
}
componentDidMount () {
this.focusSidebar()
}
componentDidUpdate () {
if (this.state.visible) this.focusSidebar()
}
focusSidebar () {
ReactDOM.findDOMNode(this.refs.sidebarRegion).focus()
}
hideSidebar(e) {
var currentTarget = e.currentTarget;
setTimeout(()=>{
if (!currentTarget.contains(document.activeElement)) {
this.setState({ visible: false })
console.log("Hiding the sidebar!");
}
}, 0);
}
render () {
return (
<div
onBlur={this.hideSidebar.bind(this)}
tabIndex='0'
className={`sidebar ${this.state.visible ? '' : 'hidden'}`}
ref='sidebarRegion'
>
<select>
<option>Foo</option>
</select>
</div>
)
}
}
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>

Related

React how to add class to parent element when child element is clicked

This is my code.
export default MainContent = () => {
handleClick = (e) => {
// This is where I got confused
};
return (
<>
<div>
<div onClick={handleClick}>1</div>
</div>
<div>
<div onClick={handleClick}>2</div>
</div>
<div>
<div onClick={handleClick}>3</div>
</div>
</>
);
}
What I want is to add a class to parent div when child element is clicked. I couldn't use useState() since I only need one element to update. Couldn't use setAttribute since it changes the same element. Is there any solution for that?
I take it you want to apply the class only to direct parent of clicked child.
create a state to oversee different clicked child div
apply the class only to direct parent of clicked* child div based on the state
make use of clsx npm package (since we don't wanna overwrite parent div styling)
you may see the working examples here: https://codesandbox.io/s/happy-babbage-3eczt
import { useState } from "react";
import "./styles.css";
import styled from "styled-components";
import classnames from "clsx";
export default function App() {
const [styling, setstyling] = useState({
status: false,
from: "",
style: ""
});
function handleClick(childNo) {
setstyling({ status: true, from: childNo, style: "applyBgColor" });
}
return (
<div className="App">
<Styling>
<div
className={
styling?.status && styling?.from == "child-1"
? classnames("indentText", styling?.style)
: "indentText"
}
>
<div
className="whenHoverPointer"
onClick={() => handleClick(`child-1`)}
>1</div>
</div>
<div
className={
styling?.status && styling?.from == "child-2"
? styling?.style
: ""
}
>
<div
className="whenHoverPointer"
onClick={() => handleClick(`child-2`)}
>2</div>
</div>
</Styling>
</div>
);
}
const Styling = styled.div`
.indentText {
font-style: italic;
}
.applyBgColor {
background-color: grey;
}
.whenHoverPointer {
cursor: pointer;
}
`;
function Item({ children }) {
const [checked, isChecked] = useState(false);
const onClick = () => isChecked(true);
return (
<div {...(isChecked && { className: 'foo' })}>
<button type="button" onClick={onClick}>{children}</button>
</div>
);
}
function MainContent() {
return [1, 2, 3].map(n => <Item key={n}>{n}</Item>);
}
I think theirs something wrong, useState and JSX will update related part, react will handling that itself, but base on logic, may you need to prevent re-render to stop issue, for example, here on handleClick, will keep re-render in each state since function will re-addressed in memory each update..
any way, base on your question, you can do that by this:
const handleClick = useCallback((e) => {
e.target.parentElement.classList.add('yourClass')
}, []);
But I believe its a Bad solution.
What I recommend is solve issue by state to keep your react life cycle is fully work and listen to any update, also you can use ref to your wrapper div and add class by ref.

How to change buttons inside react-table using if and else statement?

My reactJS web page has this react-table with 2 buttons inside, I want to change those button when a cell column status changes its value.
render() {
const columns = [
{
Header: "Actions",
accessor: "ID",
Cell: ({ value }) => (
<div style={buttonTablestyle}>
<Button
bsStyle="info"
onClick={() => {
if (window.confirm("You are approving this request after you press OK...")) {
this.updateFunctionYES(value);
}
}}
>
Approve
</Button>
<Button
bsStyle="warning"
onClick={() => {
if (window.confirm("You are rejecting this request after you press OK...")) {
this.updateFunctionNO(value);
}
}}
>
Reject
</Button>
</div>
)
}
];
}
Conditional Styling
The most basic way for conditional styling is to use state and style-objects, which will be explained below. Noteworthy alternatives for conditional styling with prettier syntax and better readability are classnames and styled components.
1) define the state
whenever you want to change a style depending on a value this value should be part of the state, since any change in the state triggers a re-render
2) define style outside the render function
when re-rendering you want to access the styles without re-defining them each time.
3) assign style depending on state
you then assign your needed style within a ternary operator, if statement or switch to use it within your html element.
Example
// define styles you want to use depending on state
const green = {
background: 'green'
}
const red = {
background: 'red'
}
class MyComponent extends React.Component {
state = {
toggle: false
}
handleClick() {
// change state on click
this.setState({
toggle: !this.state.toggle
});
}
render() {
// state change triggers re-render
// define style to be used depending on state
let myStyle = this.state.toggle ? green : red;
return <div>
<div style={myStyle}>MyApp</div>
<button onClick={this.handleClick.bind(this)}>click me</button>
</div>;
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById('app')
);
<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='app'></div>
Conditional Displaying
If you want to hide/show conditionally you can add display: none to one of the styles or you use the state to only return the jsx you want to display.
class MyComponent extends React.Component {
state = {
toggle: false
}
handleClick() {
// change state on click
this.setState({
toggle: !this.state.toggle
});
}
render() {
// state change triggers re-render
let element1 = <div>element1</div>;
let element2 = <div>element2</div>;
// define object to be used depending on state
let elementToUse = this.state.toggle ? element1 : element2;
return <div>
{elementToUse}
<button onClick={this.handleClick.bind(this)}>click me</button>
</div>;
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById('app')
);
<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='app'></div>

Change element content with onClick in React

In my application I have multiple blocks generated dynamically and each one of them has an onClick event.
My goal is to be able to change the contents of the div when the click happens.
Is there a way to do this thru event.target property of the onClick event?
Or should i create a ref for each div upon creation and then work with refs?
Or should i create an array of Div elements in component state and search&modify the element later re-rendering all divs from array?
Since blocks are generating dynamically, have onClick event on children components.
const Parent = () => {
return (
<Child content={content} contentAfterClick={content} />
<Child content={content} contentAfterClick={content} />
)
}
class Child extends Component {
constructor() {
super();
this.state ={
read: false,
};
}
render() {
if (this.state.read) {
return(
<div>{this.props.contentAfterClick}</div>
)
}
return (
<div onClick={() => this.setState({ read: true })}>
<div>{this.props.content}</div>
</div>
);
};
}
This demo illustrates how you can change the contents of a div, the text, when a click happens through the onClick and event.target object as you wanted.
You can do this through the use of refs, but normally you want to avoid refs unless absolutely necessary because there are easier ways to accomplish the same thing in React.
Also wouldn't want to keep the physical DOM nodes, HTMLDivElement, in state. Instead, keep the contents it relies upon in state (in our case a single number value), then when you change the contents it will automatically update and rerender our div nodes.
// Example class component
class Container extends React.Component {
constructor(props) {
super(props);
const blocks = [];
blocks.push(0);
blocks.push(0);
blocks.push(0);
this.state = { blocks: blocks, clickedElementContents: "" };
}
increment(event, index) {
const newBlocks = this.state.blocks;
newBlocks[index]++;
this.setState({ blocks: newBlocks, clickedElementContents: event.target.innerText });
}
render() {
return (
<div>
<div className="block" onClick={(event) => { this.increment(event, 0) }}>Click me! ({this.state.blocks[0]})</div>
<div className="block" onClick={(event) => { this.increment(event, 1) }}>Click me! ({this.state.blocks[1]})</div>
<div className="block" onClick={(event) => { this.increment(event, 2) }}>Click me! ({this.state.blocks[2]})</div>
<span>Contents of the clicked element: {this.state.clickedElementContents}</span>
</div>
);
}
}
// Render it
ReactDOM.render(
<Container/>,
document.body
);
.block {
display: inline-block;
background-color: black;
color: white;
padding: 5px;
margin-right: 10px;
}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

onMouseMove doesnot work outside of element react

I have an svg element on which I am doing onMouseDown, OnMouseMove and onMouseUp. My problem is that as soon as the user leaves the element while still holding their mouse button down, it does not register the mousemove.
I want to keep the onMouseMove event on even after user leaves the element.
Here is my code:
Class School extents React.Component {
onDragStartCircle = (e) {
//taking the initial state
}
onDragCircle = () {
// draging the element
}
onDragEndCircle = () {
// saving data to the database
}
render() {
return (
<div>
<svg>
<circle
cx={50}
cy={50}
r={10}
fill="red"
onMouseDown={this.onDragStartCircle}
onMouseMove={this.onDragCircle}
onMouseUp={this.onDragEndCircle}
/>
</svg>
</div>
);
}
}
I have also tried onDragStart, onDrag these are not working. I am using es6.
Here is an example of your code, whichs shows how to use a container to register events outside of that circle.
You should consider subscribing to the move event on drag start, and unsubscribing again on drag end, to prevent to much events firing. But this should get you started.
class School extends React.Component {
onDragStartCircle = (e) => {
console.log('drag start')
}
onDragCircle = () => {
console.log('move')
}
onDragEndCircle = () => {
console.log('drag end')
}
render() {
return (
<div class="container"
onMouseMove={this.onDragCircle}
onMouseUp={this.onDragEndCircle}>
<svg>
<circle
cx={50}
cy={50}
r={10}
fill="red"
onMouseDown={this.onDragStartCircle}
/>
</svg>
</div>
);
}
}
ReactDOM.render(
<School />,
document.getElementById("react")
);
.container {
width: 100%;
height: 100%;
}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Re-using React Components

I've been doing React lessons, but one thing I don't ever see done is reusing components. For example, if I had a button, and wanted to produce a div every time that button was clicked. How would I do it using a React component that's sole purpose is rendering a single div, And that button uses that one component to add additional divs to the page every time it's clicked?
Do you mean, something like this?
The CustomButton stateless component (presentational) just receives props and can be disabled, text can be changed and a callback can be defined. It doesn't have any own state and can be reused throughout your app where you might need a button.
The ButtonSampleApp is a container component that uses the presentional component and supplies it with a callback, and then handles that callback. To add a div in it's rendering. The ButtonSampleApp uses component state to achieve this
const CustomButton = ({ text, callback, isEnabled }) => {
return <button onClick={() => callback()} disabled={!isEnabled} type="button">{ text }</button>;
};
class ButtonSampleApp extends React.Component {
constructor() {
super();
this.state = {
divs: []
};
}
onButtonClicked() {
const { divs } = this.state;
this.setState( { divs: [...divs, { text: divs.length }] });
}
render() {
const { max } = this.props;
const { divs } = this.state;
return (<div>
<h1>Click on button to add a max of { max } divs</h1>
<div>
{ divs && divs.map( ({text}) => <div key={text}>{ text }</div> ) }
</div>
<CustomButton isEnabled={!divs || divs.length < max} text="Add button" callback={() => this.onButtonClicked()} />
</div>);
}
}
ReactDOM.render( <ButtonSampleApp max={10} />, document.querySelector('#container') );
<script id="react" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.js"></script>
<script id="react-dom" src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.js"></script>
<div id="container"></div>

Resources