Change element content with onClick in React - reactjs

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>

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>

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>

reactjs resetting props for componentWillReceiveProps

First question from my first react project.
I'm trying to figure out how to best effect a component on a specific onClick link, to be able to retrigger this effect and to not have it effected by other links on the page. The first two asks are working, but I can't seem to have other links not effect the component.
I have a DisplayLightbox component on my page accepting a couple values
<div>
<DisplayLightbox
showLightbox = {this.state.showLightbox}
lightboxValue = {this.state.travelCity}
/>
</div>
The link I want to trigger the lightbox is calling a function that sets the state (and sends the prop). This part seems to work fine.
onClick={() => this.showLightbox(el.field_city)}
showLightbox(travelCity){
this.setState({
showLightbox: true,
travelCity: travelCity,
});
}
In my DisplayLightbox component, the componentWillReceiveProps does set state to true, which adds the lb-active class in the div, which, from the css, displays the lightbox div. This seems fine.
class DisplayLightbox extends React.Component {
constructor(props) {
super(props);
this.state = {
showLightbox: false,
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.showLightbox !== this.state.showLightbox) {
this.setState({ showLightbox: nextProps.showLightbox });
}
}
closeLightbox() {
this.setState({
showLightbox: false,
});
}
render() {
var lbActive = (this.state.showLightbox === true) ? "lb-active" : ""
return <div className={"lightbox " + lbActive }>
<div className={"lightbox-close " + lbActive } onClick={() =>
this.closeLightbox()}>CLOSE</div>
Copy in lightbox
</div>;
}
}
Looking into it, I see that since props are not controlled by the component and read-only, once it's set as True and I close the div by setting the state of showLighbox back to false, the nextProps.showLightbox remains true. So, if I close it (closeLightbox) and click a different onClick on my page, it still looks into my component, sees nextProps.showLightbox is still set to TRUE and opens the lightbox.
I only want the lightbox open if that specific link is the one being clicked though. It would seem overkill to have every other link setting the state of showLightbox to false, so I'm guessing I'm not looking at this properly.
Thanks
You could just move your closeLightbox method to upper component and manage showLightbox prop from parent. Then component DisplayLightbox will have 3 props: showLightbox, travelCity and method closeLightbox.
When you move closing lightbox to parent component, event componentWillReceiveProps should be no longer needed.
It would seem overkill to have every other link setting the state of
showLightbox to false, so I'm guessing I'm not looking at this
properly.
Why not configure only the one link you want to turn on / off the lightbox then?
As i see it, a component who gets its active state from the parent or external component, should not bother to manage it in its own state.
You can manage the on / off state in the parent's state and pass down a isOn and onClose event handler to the LightBox.
Once the LightBox was clicked it will invoke the handler passed down to it and the parent will change the state of isOn to false, this will trigger a render with a new prop of isOn for the LightBox this time it's value is false.
While clicking the external link / button the parent will listen to it and change the state of isOn to true, and again isOn will passed down to LightBox with it's shiny new value of true.
Small example:
const cities = ["ny", "tlv", "ark"];
class Button extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
const { item, onClick } = this.props;
onClick(item);
}
render() {
return <button onClick={this.onClick}>{this.props.children}</button>;
}
}
class LightBox extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
const { city, onClick } = this.props;
onClick(city);
}
render() {
const { isOn } = this.props;
const css = `lightBoxStyle ${isOn && "onStyle"}`;
return (
<div
className={css}
onClick={this.onClick}>
|
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
turnedOn: []
};
this.on = this.on.bind(this);
this.off = this.off.bind(this);
}
on(city) {
const { turnedOn } = this.state;
if (turnedOn.find(c => c === city)) {
return;
}
const nextState = [...turnedOn, city];
this.setState({ turnedOn: nextState });
}
off(city) {
const nextState = this.state.turnedOn.filter(c => c !== city);
this.setState({ turnedOn: nextState });
}
render() {
const { turnedOn } = this.state;
return (
<div>
{cities.map((city, i) => {
const isOn = turnedOn && turnedOn.includes(city);
return (
<div style={{ display: "inline-block", margin: "0 10px" }}>
<LightBox city={city} isOn={isOn} onClick={this.on} />
<hr />
<Button item={city} onClick={this.off}>
Close the light!
</Button>
</div>
);
})}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
.lightBoxStyle{
border: 1px solid #eee;
width: 30px;
height: 30px;
text-align: center;
box-shadow: 0 0 4px 1px #222;
border-radius: 50%;
margin: 0 auto;
cursor: pointer;
}
.onStyle{
background-color: #333;
color: #fff;
}
<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="root"></div>
Based on the great replies, I took the close out of the child and back to the parent.
I did this by adding a lightbox-button div to the parent
<div className={"lightbox-button " + this.state.showLightbox} onClick={() => this.showLightbox()}></div>
<DisplayLightbox
showLightbox = {this.state.showLightbox}
lightboxValue = {this.state.travelCity} />
This calls the same function showLightbox, that I modified slightly to toggle
showLightbox(travelCity){
this.setState({
showLightbox: !this.state.showLightbox,
travelCity: travelCity,
});
}
Using css, I keep this lightbox-button hidden, unless this.state.showLightbox is true. When that happens, the button displays and, when click, toggles the showLightbox state, thereby removing the lightbox and button.
Not sure if this is the ideal solution, but it seems to be working.

Send to child which component child should render - React js

I have a <div> of multiple <input>, and an onClick event to open a modal window, which has to render certain div of inputs for closer zoom, but I am using this modal window for rendering a numpad too.
Is there any way to distinguish which component should be rendered? Is there a possibility to send certain component to modal component(child) and then render this component? I tried something like this -
<Modal update={this.editValue.bind(this)}>{Numpad}</Modal>
or
<Modal child={Numpad} update={this.editValue.bind(this)}/>
and then in Modal(child)
{React.cloneElement(this.props.children, { ...others})}
but it doesn't work, throwing invalid element type error. I can just use switch, inside render component of Modal to distinguish which component to render with props.type, but I want more simply way to do it, any tips?
As far as I understand you correctly, you try to create a tabbed interface within a modal window, so that you could show different content depending on currently active tab. I can see the following solution for that problem.
First, we create the basic component that will hold Modal component and its content:
class App extends React.Component {
render() {
return (
<Modal>
<div>The content of the first tab</div>
<div>The content of the second tab</div>
<div>The content of the third tab</div>
</Modal>
);
}
}
Each child (div) component above represents the content for each tab in Modal.
Now let's create Modal component itself:
class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
tabIndex: 0
};
}
switchTabs(tabIndex) {
this.setState({
tabIndex
});
}
render() {
return (
<div>
<button onClick={this.switchTabs.bind(this, 0)}>1</button>
<button onClick={this.switchTabs.bind(this, 1)}>2</button>
<button onClick={this.switchTabs.bind(this, 2)}>3</button>
<div>
{React.Children.map(this.props.children, (child, index) =>
index === this.state.tabIndex && child
)}
</div>
</div>
)
}
}
We keep the index of active tab (tabIndex) as a part of component's local state. We also show three buttons for switching between these tabs.
Finally, we iterate over components children using React.Children.map and show that child whose index corresponds to current tabIndex.
Try the snippet below.
class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
tabIndex: 0
};
}
switchTabs(tabIndex) {
this.setState({
tabIndex
});
}
render() {
return (
<div>
<button onClick={this.switchTabs.bind(this, 0)}>1</button>
<button onClick={this.switchTabs.bind(this, 1)}>2</button>
<button onClick={this.switchTabs.bind(this, 2)}>3</button>
<div>
{React.Children.map(this.props.children, (child, index) =>
index === this.state.tabIndex && child
)}
</div>
</div>
)
}
}
class App extends React.Component {
render() {
return (
<Modal>
<div>
<img src="https://avatars2.githubusercontent.com/u/4226954?v=3&s=400" height="200" />
</div>
<div style={{ height: '200px', width: '200px', marginTop: '5px' }}>The second tab</div>
<div>
<img src="https://avatars3.githubusercontent.com/u/810671?v=3&s=400" height="200" />
</div>
</Modal>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
<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="app"></div>

Resources