How can I change a specific component in React on an eventHandler - javascript

What i'm trying to do should be fairly simple but it seems I am not able to get reference to the specific component using this
So here I have my App.js
import React, { Component } from 'react';
import CoolBox from './coolBox.js';
import './App.css';
class App extends Component {
changeColor(){
$(this).css('background','blue');
}
render() {
return (
<div className="App">
<CoolBox changeColor={function(){ this.changeColor() }.bind(this)} />
<CoolBox changeColor={function(){ this.changeColor() }.bind(this)} />
<CoolBox changeColor={function(){ this.changeColor() }.bind(this)} />
</div>
);
}
}
export default App;
And then here is CoolBox.js which is just a simple box with a background of red:
import React, { Component } from 'react';
import $ from 'jquery';
class CoolBox extends Component {
render() {
return (
<div onClick={this.props.changeColor} className="box"></div>
);
}
}
export default CoolBox;
Which simply looks like this:
Now what I am trying to achieve is when you click on any of the 3 boxes the background color will change just on that specific box that was clicked.
It seems I cannot use any jquery methods if $(this) cannot be referenced. So how can I achieve this simple function within React?

You don't need jQuery for this.
There are couple of way to reference components in the DOM and there are couple of patterns of such components(controlled and uncontrolled) you should read about it.
As for you solution, this is a simple solution just to get you start with.
On event handlers you can access the event as an argument.
changeColor(e) as e is the object that holds the event information as well as the target (the div you clicked in your case).
So basically what you can do in App.js is this:
class App extends React.Component {
constructor(props){
super(props);
this.changeColor = this.changeColor.bind(this);
}
changeColor(e){
e.target.style.background = "blue";
}
render() {
return (
<div className="App">
<CoolBox changeColor={this.changeColor} />
<CoolBox changeColor={this.changeColor} />
<CoolBox changeColor={this.changeColor} />
</div>
);
}
}
Please note
As you can see i bind the handler in the constructor instead of in the render method. that way you only bind it once and not on each render call witch will create a new instance on each render. that is better for performance.

this in a React component does not refer to the DOM element, rather it refers to the Component instance, as the DOM of a given component can change in arbitrary ways as a result of changing state or props.
As mentioned by #Chris in the comments above, you shouldn't really be using jQuery with your React components, unless you have a really good reason, and you know what you're doing.
Instead, you should use Component state to declare what you want, and then reflect your component's state in your render() method.
Here's an example
class CoolBox extends React.Component {
constructor(...args) {
super(...args);
this.state = {
color: 'red'
};
this.changeColor = this.changeColor.bind(this);
}
changeColor() {
this.setState({
color: this.state.color === 'red' ? 'green' : 'red'
});
}
render() {
return <div
onClick={this.changeColor}
className="box"
style={{backgroundColor: this.state.color}}
></div>
}
}
class App extends React.Component {
render() {
return (
<div>
<CoolBox />
<CoolBox />
<CoolBox />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
.box {
height: 100px;
width: 100px;
margin: 10px;
display: inline-block;
}
<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>

Related

How to call parent method from React Child using Super call?

I want to be able to abstract the way React component logic works separate the view logic from the handlers
class SuccessLabelWithIcon extends Label{
constructor(props){
super(props);
this.className = this.className + ' success-label';
}
render(){
return <div>
<div onClick={super.onClick}>▲</div> // this works but fires initially too in StackBlitz but not in SO not sure why, but changing to clickHandler dosent work.
</div>
}
}
class Label extends React.Component{
constructor(props){
super(props);
this.className='plain-label';
}
clickHandler = () => {
console.log('Inherited');
}
onClick() {
console.log('Inherited');
}
render(){
return <span className={this.className}>
{this.props.children}
</span>
}
}
// Render it
ReactDOM.render(
<SuccessLabelWithIcon/>,
document.getElementById("react")
);
<div id="react"></div>
<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>
Working StackBlitzLink
Am I wrong with this approach? If No why is this not working for clickHandler, what I am looking at is extending component to have logic and the render functions as separate all the Button clicks and methods will be executed in the parent so that we can have separation of logic and then maybe share code across React and RN, but that is secondary?
My best bet is the clickHandler is attached to some prototypical property of the parent class as it a bound method and the click works as it a normal method not bound to the component but can we bypass this and can we mitigate this problem? And fire method only on click
Your approach is one which is not recommend and maybe even an anti-pattern to react's compositional model. Refer to Composition vs Inheritance section in the docs to see why composition is better over inheritance in react and how to handle some of the problem with composition where developers reach for inheritance. In your case, you should look at Specialization
You should almost always extend React components from React.Component.
class SuccessLabelWithIcon extends Label{
constructor(props){
super(props);
}
render() {
return (
// Specialization
<Label>Success</Label>
)
}
}
class Label extends React.Component{
constructor(props){
super(props);
}
clickHandler = () => {
console.log('Inherited');
}
onClick() {
console.log('Inherited');
}
render(){
return <span className="plain-label" onClick={this.onClick}>
{this.props.children}
</span>
}
}
<div id="react"></div>
<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>
Here is a forked working version of your stackblitz.
Working StackBlitzLink
Yes as u have stated your approach is not correct, in React you want to share code usage by components composition instead of inheritance.
I'm gonna use something similar to your example to help you understand the concept, lets say you want to create a base component called IconButton which will be a button with text alongside an icon, and you want to reuse that component to create two more components DangerIconButton (this is a button with an icon that is meant to represent a dangerous situation like a delete operation), and you want to create a SuccessfulIconButton (a button where you want to represent a desired action)
now based on our requirements we see that the functionality across all the three components is the same, basically a click handler but what is different is the visual representation (the icon and the styles applied to the button).
lets create our base component
class IconButton extends React.Component{
render(){
return <button onClick={props.onClick} style={props.style}>
<img src={props.icon} />
<span>button text</span>
</button>
}
}
now our two other components will be much simpler, and will use that base component
class SuccessfulIconButton extends React.Component{
render(){
// notice how i'm creating this component by composition of other components
// happyIcon and green are what makes this a successful butoon
return <IconButton onClick={props.onClick} style={{background: 'green'}} icon={happyIcon} />
}
}
class DangerIconButton extends React.Component{
render(){
// notice that i'm getting the onClick handler here through props, because u want the parent components of this component to have their specific handlers
return <IconButton onClick={props.onClick} style={{background: 'red'}} icon={dangerIcon} />
}
}
of course here u want better naming, and u still want to do some common styling for all your IconButtons like padding, and other stuff. but hopefully the concept came through
As mentioned by #hassaan-tauqir, its usually preferred to use composition rather than inheritance. But if you really want to do it, you can use the below code
class Label extends React.Component{
constructor(props){
super(props);
this.className='plain-label';
}
clickHandler = () => {
console.log('Inherited');
}
onClick() {
console.log('Inherited');
}
render(){
return <span className={this.className}>
{this.props.children}
</span>
}
}
class SuccessLabelWithIcon extends Label{
constructor(props){
super(props);
this.className = this.className + ' success-label';
}
render(){
return (<Label><div>
<div onClick={this.clickHandler}>▲</div> // this works but fires initially too in StackBlitz but not in SO not sure why, but changing to clickHandler dosent work.
</div></Label>);
}
}
// Render it
ReactDOM.render(
<SuccessLabelWithIcon/>,
document.getElementById("react")
);
<div id="react"></div>
<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>
The reason clickHandler doesn't work is because you are using arrow functions to define it (basically it's doesn't have your SuccessLabelWithIcon context, and has instead the context of Label).
You can make it a simple method or you can simply use it with this instead of super (since you are extending the component).
class Label extends React.Component{
constructor(props){
super(props);
this.className='plain-label';
}
clickHandler = () =>{
console.log('Inherited');
}
onClick() {
console.log('Inherited');
}
render(){
return <span className={this.className}>
{this.props.children}
</span>
}
}
class SuccessLabelWithIcon extends Label{
constructor(props){
super(props);
this.className = this.className + ' success-label';
}
render(){
return (<Label><div>
<div onClick={this.clickHandler}>▲</div> // this works but fires initially too in StackBlitz but not in SO not sure why, but changing to clickHandler dosent work.
</div></Label>);
}
}
// Render it
ReactDOM.render(
<SuccessLabelWithIcon/>,
document.getElementById("react")
);
<div id="react"></div>
<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>
Hope this helps you

In React JavaScript, How to call a class inside another class?

I began to learn JavaScript and React these days, I tried to draw some grids in a website and met a problem like this:
Everything works fine when I code like this:
export default class PathfindingVisualizer extends Component {
constructor(props) {
super(props);
this.state = {
nodes: [],
};
}
componentDidMount() {
const nodes = getInitialGrid();
this.setState({ nodes });
}
render() {
const { nodes } = this.state;
console.log(nodes);
return (
<>
<p style={{ fontSize: 40 }}>Visualize Algorithms</p>
<br />
<br />
<div className="node-container">{nodes}</div> // HERE WORKS FINE
</>
);
}
}
And the website turned out to be like this which is fine:
But when I changed the code like this:
render() {
const { nodes } = this.state;
console.log(nodes);
return (
<>
<p style={{ fontSize: 40 }}>Visualize Algorithms</p>
<br />
<br />
<NodeContainer>{nodes}</NodeContainer> // HERE
</>
);
}
}
The grids just disappear, and nothing in <body>:
Could anybody help me? I can't figure out why this is happening.
Class NodeContainer and Node are like this:
export default class NodeContainer extends Component {
render() {
return <div className="node-container"></div>;
}
}
export default class Node extends Component {
render() {
return <div className="node-item"></div>;
}
}
Hey, thank you guys for the answers:) this is my first time to ask a question here. I solved the problem by adding {this.props.xxxxx} as you said and it works.
Corrected codes as following:
...
<br />
<br />
<NodeContainer nodes={nodes}></NodeContainer> // HERE
</>
...
the NodeContainer class:
export default class NodeContainer extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return <div className="node-container">{this.props.nodes}</div>; //HERE
}
}
I didn't use 'this.props.children', but will check out later. I skipped the basic tutorial so I didn't understand how to pass params to class, I checked this video to help myself quickly understand this:
https://www.youtube.com/watch?v=ICmMVfKjEuo&list=PLN3n1USn4xlntqksY83W3997mmQPrUmqM&index=5&t=0s
For this you need to call children inprops
export default class NodeContainer extends Component {
render() {
return <div className="node-container">{this.props.children}</div>;
}
}
I don't see where the Node class is being referenced so I'm not sure that's relevant.
Your issue is that the your passing the nodes component to the NodeContainer component, but not rendering it in NodeContainer. You should look into how props are passed to components - they appear as this.props.children on the component. Your code should look like this.
export default class NodeContainer extends Component {
render() {
return <div className="node-container">{this.props.children}</div>;
}
}
If you're wondering how nodes appears as this.props.children, it's because of how React treats components. You can achieve the same thing by passing it into children explicitly as a prop.
Dude, in reactJS, there's should be data to be pass from your Parent element to your Children element.
In your case to be able to show the data you want,
you need to pass your state from the <PathFindingVisualizer /> to your <NodeContainer />, which you have done it by using node as a children between <NodeContainer /> tag. And you forget the second step,
You need to access the data you have passed inside <NodeContainer /> class you made. How? just access it by using this.props.children.
here's the example.
export default class NodeContainer extends Component {
render() {
return <div className="node-container">{this.props.children}</div>
}
}
Problem solved.
as a reference see this. https://learn.co/lessons/react-this-props-children

Update State of a Component from Another in ReactJS

I'm following this article (original implementation Sibling Sibling):
Update state cross component
The example works perfectly. But when I try to separate each class to each .js file, then using import/export to call/bind each other. It (the updating state) doesn't work anymore.
The structure like this:
Sibling1.js
import React, { Component } from 'react';
<-- some declare style -->
export function updateText(text) {
this.setState({text})
}
export class Sibling1 extends Component {
render() {
return (
<div>
<div style={{ ...style.topLabel, color: secondaryColor }}>I am Sibling 1</div>
<input style={style.textBox} type="text"
placeholder="Write text" onChange={(e) => updateText(e.target.value)} />
</div>
)
}
}
Example.js
import React, { Component } from 'react';
import * as sibling1 from './Sibling1'; //is this good?
import {Sibling1} from './Sibling1'; //is this good?
<-- some declare style, variable -->
class Sibling2 extends Component {
constructor(props) {
super(props)
this.state = {
text: "Initial State"
}
sibling1.updateText = sibling1.updateText.bind(this) //is this good binding?
}
render() {
console.log('Sibling2.state : ', this.state);
return (
<div>
<div style={{ ...style.topLabel, color: primaryColor }}>I am Sibling 2</div>
<div style={style.label}>{this.state.text}</div>
</div>
)
}
}
class Example3 extends Component {
render() {
return (
<div>
<Sibling1 />
<Sibling2 />
</div>
)
}
}
export default Example3;
I am simply expecting Sibling1 can change the state of Sibling2 (like the original implementation), but cannot.
I guess that my bind(this) doesn't bind the right context.
Can somebody tell me what are differences between the original implementation (article above) and my approach (separate to multi .js files)?
updateText() should be bound to a component. I'm not sure what you're trying to achieve here but updateText() might not work in Sibling1 if context changes.
You could try binding updateText() in both components (already bound in Sibling2).
import React, { Component } from 'react';
export function updateText(text) {
this.setState({text})
}
export class Sibling1 extends Component {
constructor() {
updateText = updateText.bind(this)
}
render() {
return (
<div>
<div style={{ ...style.topLabel, color: secondaryColor }}>I am Sibling 1</div>
<input style={style.textBox} type="text"
placeholder="Write text" onChange={(e) => updateText(e.target.value)} />
</div>
)
}
}
Usually state is controlled in parent component if two child components need to share the state and only the handler is passed down to the chilren.
React kinda forces you to use a one way data flow. So you can't just update the state of Sibling1 from within Sibling2.
As Dinesh Pandiyan mentions in his example that you normally would have a parent component that controls the state of both the siblings. Your code would then look like this:
Sibling1.js
import React, { Component } from 'react';
<-- some declare style -->
export class Sibling1 extends Component {
function updateText(text) {
// Use updateText function from props.
// Props are like state but not controlled by the component itself
// The value is passed to the component from outside
this.props.updateText(text)
}
render() {
return (
<div>
<div style={{ ...style.topLabel, color: secondaryColor }}>I am Sibling 1</div>
<input style={style.textBox} type="text"
placeholder="Write text"
onChange={(e) => this.updateText(e.target.value).bind(this)} />
</div>
)
}
}
Example.js
import React, { Component } from 'react';
import { Sibling1 } from './Sibling1'; // This is good.
import Sibling1 from './Sibling1'; // This is also possible if you use export default class instead of export class
<-- some declare style, variable -->
class Sibling2 extends Component {
// Use same function as in Sibling1.
function updateText(text) {
this.props.updateText(text)
}
render() {
return (
<div>
<div style={{ ...style.topLabel, color: primaryColor }}>I am Sibling 2</div>
<div style={style.label}>{this.props.text}</div> // changed state to props
</div>
)
}
}
class Example3 extends Component {
constructor(props) {
super(props);
this.state = {
text: "Initial state"
};
}
// Control state from parent component
function updateText(
this.setState({ text: text });
}
render() {
return (
<div>
<Sibling1 updateText={this.updateText.bind(this)}/>
<Sibling2 updateText={this.updateText.bind(this)} text={this.state.text} />
</div>
)
}
}
export default Example3;

How to pass an element between two components in reactJS

I recently have begun learning reactjs and I am having a hard time comprehending state and how it's used. I have built two stateless components (boxOne and boxTwo) and I have a property "Move Me" that I would like to pass between the two components on the click of a button (MoveButton). Below is the code to where I reached to before getting stuck
class MoveButton extends React.Component {
render() {
return (
<button className="thebutton">
Click To Move
</button>
);
}
}
class BoxOne extends React.Component {
render() {
return (
<div className="boxOne-container">
{this.props.name}
</div>
);
}
}
class BoxTwo extends React.Component {
render() {
return (
<div className="boxTwo-container">
</div>
);
}
}
function App() {
return (
<div>
<BoxOne name="Move Me" />
<BoxTwo />
<MoveButton />
</div>
);
}
ReactDOM.render(<App />,document.getElementById('container'));
Okay, so here is a codepen with everything working.
Here is the code for future generation in the event codepen dies before S-O (I think you can run it here as well??).
class Box extends React.Component {
render(){
return (
<div>
{this.props.name ? this.props.name : "nothing"}
</div>
);
}
}
class MoveButton extends React.Component {
render(){
return(
<button onClick={this.props.on_click_handler}>
Click Me
</button>
);
}
}
class App extends React.Component {
constructor(props){
super(props);
this.state = {
first_button: true
};
this.on_click_handler = this.on_click_handler.bind(this);
}
on_click_handler(){
this.setState({
first_button: !this.state["first_button"]
});
}
render() {
return (
<div>
<Box name={this.state["first_button"] ? "Move Me": null} />
<Box name={!this.state["first_button"] ? "Move Me": null} />
<MoveButton on_click_handler={this.on_click_handler} />
</div>
);
}
}
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>
So, anyways... here's the explanation.
Basically what you want to do is have the higher level component deal with the state. In this case, we're talking about App. Eventually you'll start to learn where state should go, but generally you want it to be at the highest point that makes sense. Basically, in this case since the App component has the thing (the button) that is changing the state of the two Box we want the state there.
I make the actual function that deals with the click inside the App component, and pass it down to the sub component, MoveButton. I do this because the function is changing state in the App component, so it has to be there. I also had to bind the this in the constructor, which is this line: this.on_click_handler = this.on_click_handler.bind(this);. This just makes sure that this is always referencing the correct thing inside that function.
Then in that handler function I change the components state, which causes a re-render. I use the ternary operator to see which instance of Box I should be passing the "Move me" to. I also use the ternary operator in Box itself to either put the name, or "nothing" but you can change that whatever.
Hope that helps.
P.S: You don't need two different component classes for Box. They're the same thing, so just reuse the same component, but make two instances of it. Which is what I did here.
First off I'd strongly suggest to read the entire react documentation: https://facebook.github.io/react/docs/hello-world.html (or at the very least, to start off the whole quick start section, which covers all the basic you need). It covers pretty much all of react (React has quiet a small scope!).
You need to have some kind of state. Currently your class components (MoveButton, BoxOne and BoxTwo) have access to state but don't use it. Your App component defined as function does not have access to any kind of own state.
Your state needs to be in a common parent component, which you can then pass down to child components as props. The child components may be stateless. In your case that would be the App Component, which you could use a class for instead to make react state available, while the other three components you could rewrite to be stateless functions.
Now I don't understand what exactly you want to happen, I'll just assume you want to move the "Move me" text from one Box to the other on clicking the button. Therefore both boxes have the ability to display text, controlled by the parent. Both boxes could have a react prop called 'name', received by the parent (App). The button itself needs to emit an event (callback), defined in the parent and passed down to the button as prop. I'll call that prop 'handleEvent'.
The implementation could look like such:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
function BoxOne(props) {
return (
<div>BoxOne: {props.name}</div>
);
}
function BoxTwo(props) {
return (
<div>BoxTwo: {props.name}</div>
);
}
function MoveButton(props) {
return (
<button onClick={props.handleEvent}>Click to Move</button>
);
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
boxOneName: 'Move me',
boxTwoName: ''
};
this.handleEvent = this.handleEvent.bind(this);
}
handleEvent() {
this.setState({
boxOneName: this.state.boxTwoName,
boxTwoName: this.state.boxOneName
});
}
render() {
return (
<div>
<BoxOne name={this.state.boxOneName}/>
<BoxTwo name={this.state.boxTwoName}/>
<MoveButton handleEvent={this.handleEvent}/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
Everything used in the example is adressed within the react quick start guide.
Let me know if anything is still unclear :)!

Instance of reactJs component to render a component

Can I use an instance of a reactJS component to render a component.
Eg, Let's say my reactJS component is
class myComponent extends Component{
constructor(props){
super(props)
this.state = {
next:false
}
this.alertSomething = this.alertSomething.bind(this);
this.showNext = this.showNext.bind(this);
}
showNext(){
console.log('wow');
console.log(this.state, this, this.state.next);
this.setState({next:true});
}
alertSomething(){
alert('Alert Something')
console.log(this.state, this, this.state.next);
this.setState({next:true});
}
render(){
return(
<div className='column'>
</div>
)
}
}
export default myComponent
Now, inside my another component can I do;
let x = new displayContent.renderComponent();
render(
<x />
//or
<x.render />
)
// I tried both it didn't work, I thought there mush be some other way to achieve this, after all every component is just a javascript object.
Also at the same time, can I call function to make change in its state. Like.
x.someFunction();
where someFunctino is inside that react component, doing setState.
Is it possible? OR am I missing something?
Edit: I clearly understand that when you want to render a react component, you can always do, <component />.
This question is just out of curiosity, can this be done? if not, then why?, I mean how is that different from other javascript objects.
Well, you can use the React.createElement method to render a component:
React.createElement(Component, params)
but with JSX, this is the same:
<Component />
Refer to Multiple components in the React documentation.
This is not how you're supposed to use React. You don't have to handle object instantiations ; React do this for you. Use composition instead.
render() {
return (
<myComponent />
)
}
Also, if you want to set the state of a child component from a parent component, you should probably move the logic in the parent.
Probably you are looking for something like this.
import React, { Component } from "react";
import CamCapture from './CamCapture.js';
export default class ProctorVideoFeed extends Component{
constructor(props) {
super(props);
this.Camera = React.createElement(CamCapture);
}
//this.handleVideoClick = this.handleVideoClick.bind(this);
render(){
return(
<div>
<span>{this.Camera}</span>
<button onClick = {this.Camera.StopRecording}>Stop</button>
</div>
)
}
}
Here StopRecording is a function defined inside CamCapture class.

Categories