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
Related
I have two files First.js and Second.js they are not child and Parent they have a lot of functions inside them. I want to use 1 function which is inside First.js into another file Second.js.
First.js
export default Header extends React.Component{
constructor(){
super();
}
updateXS(e){
alert('Test');
}
}
Second.js
export default Second extends React.Component{
constructor(){
super();
}
solveVariable() {
updateXS()// from first file
}
render(){
return (
<div className="displayinline col-md-12 ">
<button onClick={self.solveVariable.bind(this)}>Solve</button>
</div>
);
}
}
On click of Solve button in need to call updateXS() there are other functions render() also present in the first file.
You need to bubble up the action to a container
export default Second extends React.Component{
constructor(){
super();
}
solveVariable() {
this.props.handleSolveVariable();
}
render(){
return (
<div className="displayinline col-md-12 ">
<button onClick={self.solveVariable.bind(this)}>Solve</button>
</div>
);
}
}
Then the container can handle the solving part, set a state and pass it to the children.
class Container extends Component {
solveThis(e) {
// ... calculate
return result;
}
handleSolveVariable = (e) => {
const result = this.solveThis(e);
this.setState({result})
}
render() {
return (
<div>
<First result={this.state.result}/>
<Second result={this.state.result} handleSolveVariable={this.handleSolveVariable}/>
</div>
);
}
}
You should keep in mind the «actions up, data down» philosophy of react and not rely on observing events like in Jquery.
The children can react based on the new state an rerender.
You can also use componentWillReceiveProps to compare new props and react to it.
You can bubble up or create an utils function file and then export them. If you think is a function that you might use across your application it might be the best solution.
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 :)!
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>
My React app has several similar custom buttons that perform different tasks. The UI is similar among all the buttons, what changes is the action each one must trigger and the title.
The working code for the parent Component containing the buttons is (similar) to the following:
class Page extends Component {
constructor(props) {
super(props);
}
action1(){
//... do stuff...
}
action2(){
//... do stuff...
}
render(){
return(
<div className="table-row">
<div className="table-cell">
<div className="button"
onClick={this.action1.bind(this)}>{"button1"}
</div>
</div>
<div className="table-cell">
<div className="button"
onClick={this.action2.bind(this)}>{"button2"}
</div>
</div>
</div>
);
}
}
Is it possible to pass a method to the child component the same way it is done for a variable value? I want to turn it into something like this:
class Page extends Component {
constructor(props) {
super(props);
}
action1(){
//... do stuff...
}
action2(){
//... do stuff...
}
render(){
return(
<div className="table-row">
<div className="table-cell">
<CustomButton action={this.action1.bind(this)} title={"button1"}/>
</div>
<div className="table-cell">
<CustomButton action={this.action2.bind(this)} title={"button2"}/>
</div>
</div>
);
}
}
class CustomButton extends Component{
constructor(props){
super(props);
}
render() {
return (
<div className="table-cell"><div className="button"
onClick= {this.props.action}>{this.props.title}
</div>
);
}
}
What would be the correct way to handle this situation and the theory behind it?
I'm using React with Meteor, in case it makes a difference.
You can pass props to components. Passed props can have any data type of javascript.
In your case, you want to pass an action props which has a function as the value. Then you access action props in your component and use it.
In short, there is no theory behind it. What you are doing is correct. This is how react handles passing data to other components. Note that this is not the only way to pass data to child components.
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.