React stripping out onclick - reactjs

I have the following React component:
class PlayerRow extends Component {
activateKey(){
$(this).addClass('active')
}
render() {
return (
<div className="row-par">
<div className="details-cont">
</div>
<div className="key-cont">
<div onClick={this.activateKey} className="key"></div>
<div onClick={this.activateKey} className="key"></div>
<div onClick={this.activateKey} className="key"></div>
</div>
</div>
)
}
}
I am trying to perform the function activateKey on click of on of the nested divs but whenever I render out the app my onclick attribute is stripped out of all the divs. Why would this be?

React binds event listeners itself, so onClick does not directly result in onclick attribute in DOM. However, your code actually works (see snippet).
class PlayerRow extends React.Component {
activateKey() {
console.log('ddd')
}
render() {
return (
<div className="row-par">
<div className="details-cont">
</div>
<div className="key-cont">
<div onClick={this.activateKey} className="key">asd</div>
<div onClick={this.activateKey} className="key">qwe</div>
<div onClick={this.activateKey} className="key">zxc</div>
</div>
</div>
)
}
}
ReactDOM.render(
<PlayerRow/>,
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>
I suggest 2 things though, use .bind(this) for action handler or even better, use arrow function as it inherits scope by default.
From react docs:
handleClick = () => {
console.log('this is:', this);
}
see: https://facebook.github.io/react/docs/handling-events.html
Second thing - don't use jQuery. You're using React to manage DOM, and jQuery produces side-effects by default, so, it will interfere with how react works. React renders your view according to component's state or props automatically, with it's optimizations which are awesome, and jQuery doesn't edit neither props nor state, so you don't take advantage of any react's cool stuff.
For what you want to achieve, you could give your action handler a parameter which tells you which block has been clicked onClick={() => activateKey(1)}. In the handler save the active id on local state, and give your blocks the class conditionally depending on if their id matches the key id. That's my idea.

Using ES6 arrow function or .bind() in the render method adds unnecessary overhead to your components. You really should .bind(this) to your methods in the constructor like such:
import React, { Component } from "react";
export default class Demo extends Component {
constructor(props) {
super(props);
this.activateKey = this.activateKey.bind(this);
}
activateKey() {
console.log("ddd");
}
render() {
return (
<div className="row-par">
<div className="details-cont"></div>
<div className="key-cont">
<div onClick={this.activateKey} className="key">asd</div>
<div onClick={this.activateKey} className="key">qwe</div>
<div onClick={this.activateKey} className="key">zxc</div>
</div>
</div>
);
}
}
If you do this, the bind operation is only performed once per method during the class initialization phase. If you use ES6 arrow functions or .bind(this, etc..) inside of your render method, the binding will be executed from each element that has an onClick event, on every re-render.
The only time you should use .bind(this, etc..) or an ES6 arrow function inside of the render method is if you need to pass something other than a click event to a function.

Related

How to wrap a react node by another node inside a react component

I am using owl-react-carousel in my react app. owl carousel injects some div after component render. I want to create a div using react-dom and then want to wrap owl carousel's injected div. trying to make more clear
class Items extends Component {
render() {
return (
<div className="item"></div>
)
}
}
after render this component owl carouse injects some node inside item class. Like bellow
<div className="item">
<div className="owl-nav"></div>
</div>
Now i want to create my own div and then want to wrap owl-nav div
expected : want to create new node <div className="custom-nav"></div>
component should look like
<div className="item">
<div className="custom-nav">
<div className="owl-nav"></div>
</div>
</div>
I could made it like be
componentDidMount() {
const appNode = ReactDOM.findDOMNode(this);
var searchingElement = appNode.querySelector("div.owl-nav");
var wrapperElement = document.createElement("div");
wrapperElement.setAttribute("class", "custom-nav");
searchingElement.parentNode.insertBefore(
wrapperElement,
searchingElement
);
wrapperElement.appendChild(searchingElement);
}
It meets my requirement but i dont want to use document direct in react so tried react.createElement but then it says wrapper is not a node

Persist dom state on route change

I have two components each on separate routes. I would like to know how I can keep the DOM elements in the same state on route change. For example I would like for all the DOM elements to have the same css classes applied as before the route change when navigating back to the same component.
I have tried redux persist and using nested routes with switch but none of these seem to work. From the research I have done it appears that React always mounts and unmount the component on route change and I haven't' been able to find a way to prevent this happening.
I would like for the red background color to remain when going back to test1.
class test1 extends React.Component {
constructor(props) {
super(props);
}
addClassFucn = event => {
$(event.target).parent().css("background-color", "red")
}
renderButton() {
return (
<div>
<div>
<button onClick={this.addClassFucn}>Click me</button>
<Link to="/test2" className="ui button primary back" >
test2
</Link>
</div>
</div>
)
}
render() {
return (
<div>
<div>This is test 1{this.renderButton()}</div>
</div>
);
}
}
export default test1;
class test2 extends React.Component {
constructor(props) {
super(props);
}
renderButton() {
return (
<div>
<Link to="/test1" className="ui button primary back" >
back
</Link>
</div>
)
}
render() {
return (
<div>
<div>This is test 2{this.renderButton()}</div>
</div>
);
}
}
export default test2;
It really depends on the logic of how you maintain the state of your component.
Redux persist should work. Just persist all the state that affects how the DOM currently displayed. Afterwards, inside the component, you should do a check whether there is a persisted state or not. If it is then you shouldn't do any change and just render.
You can use react's shouldComponentUpdate() which by default returns true allowing the component to re render. If useful than you can add the logic to return false which won't all the component to re-render. This is not recognized as best practice though you can refer this link for more details.

Is there any problem to have many ReactDOM.render() in a ReactJS Project?

We are working on a Laravel project and it is written totally with JavaScript/HTML/JQuery. We are considering migrating to React as Laravel supports it. Is it OK to have many ReactDOM.render() ( per component) in the initial steps so we can progressively and fully convert our code base in the long run?
Is it necessary to access ref or just we can render each component like this:
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
ReactDOM.render(<App/>, document.querySelector("#form"))
and use it like this for every component:
// some html code and then
...
<div id="form"> </div>
<div id="modal"> </div>
<div id="navbar"> </div>
...
// rest of html code
Yes this is totally fine, when you call ReactDOM.render multiple times it will basically just triggers a diffing, very similar to the render method of a class component.
I actually wrote an article (with a tutorial) about "Integrate React with other applications and frameworks" that talks exactly about that topic.
The bottom line of this approach is that you can expose a global object with mount and unmount functions of your widget / mini-app and inside it call ReactDOM.render or ReactDOM.unmountComponentAtNode.
window.MyComponent = {
mount: (props, container) => {
ReactDOM.render(<Component {...props} />, container);
},
unmount: (container) => {
ReactDOM.unmountComponentAtNode(container);
}
}
Then in another part in the page you can call these functions, no matter what library or framework you are using obviously.
The beauty here, is that other react applications can use it as if it was a real component:
class MyComponentWrapper extends PureComponent {
// create a ref so we can pass the element to mount and unmount
myRef = React.createRef();
componentDidMount() {
// initial render with props
window.MyComponent.mount(this.props, this.myRef.current);
}
componentDidUpdate(prevProps) {
if(prevProps !== this.props){
window.MyComponent.mount(this.props, this.myRef.current);
}
}
componentWillUnmount(){
window.MyComponent.unmount(this.myRef.current);
}
render() {
return <div ref={this.myRef}></div>
}
}
Hope it helps.
Yes, that's completely fine. React is designed for gradual adoption.

Element not being displayed when div is removed in react

I am quite new to react but I am trying to pass element from the Course component to my Coursebox component. I am doing this successfully but the button is being displayed since I am not passing that with this.prompt. I would like to remove the div id="course" from the HTML because I only want the course component to be displayed within the couresebox component.
This is my JSX code
class Course extends React.Component {
render() {
return (
<div className="course">
<h3>{this.props.coursename}</h3>
<h4> {this.props.status} {this.props.progress}</h4>
<button>Start exercise</button>
</div>
);
}
}
ReactDOM.render( < Course />, document.getElementById('course'));
class Coursebox extends React.Component {
render() {
return (
<div>
<Course coursename="Negotiation" progress= "20%" status="Progress"/>
<Course coursename="Frontend" progress="56%" status="Progress"/>
<Course coursename="Food" status="Progress" progress="43%"/>
</div>
);
}
}
ReactDOM.render( < Coursebox />, document.getElementById('coursebox'));
and HTML
<header id="header">
</header>
<h3 id="search"></h3>
<div id="coursebox"></div>
<div id="course"></div>
When I remove the nothing is being displayed on the page apart from the header. Since I am passing the element from Course to the Coursebox component, shouldn't I be able to remove the course div from the HTML?
If it is still needed, why is that?
Is there a way for me to only display the button when a course name is being passed?
Thanks :)
Avoid rendering the course component. Ideally there should be just one render method and all other components should be called from that component. So render on CourseBox component only.
class Course extends React.Component {
render() {
console.log("hey")
return (
<div className="course">
<h3>{this.props.coursename}</h3>
<h4> {this.props.status} {this.props.progress}</h4>
<button>Start exercise</button>
</div>
);
}
}
class Coursebox extends React.Component {
render() {
return (
<div>
<Course coursename="Negotiation" progress= "20%" status="Progress"/>
<Course coursename="Frontend" progress="56%" status="Progress"/>
<Course coursename="Food" status="Progress" progress="43%"/>
</div>
);
}
}
React.render(<Coursebox />, document.getElementById('coursebox'));
Working JS fiddle http://jsfiddle.net/kmbw9wgt/3/
1.) See this line
ReactDOM.render( < Course />, document.getElementById('course'));
You are explicitly asking react to render Course in a div which has id 'course'.
If you remove this, it will not render separately, but only from within Coursebox element.
Then you can safely remove that div.
2.) Yes, you can only show button when course name is passed. Using If condition ternary operator. Add your button in following way:
<h3>{this.props.coursename}</h3>
<h4> {this.props.status} {this.props.progress}</h4>
{ this.props.coursename ? (<button>Start exercise</button>): null}

Bootstrap collapse with react js

Hi I'm trying to use bootstrap collapse inside a react view and it's not working. It's very simple but I don't understand what's going on.
return (<div>
<button className="btn" type="button" data-toggle="collapse" data-target="#collapseExample" aria-expanded="true" aria-controls="collapseExample">
ButtonClickthis!
</button>
<div className="collapse" id="collapseExample">
<div className="well">
...blablablacontent
</div>
</div>
</div>);
Bootstrap will not work out of the box for react components, since it parses the DOM on load and attaches event listeners etc. You can try something like react-bootstrap or manually triggering inside the componentDidMount lifecycle.
– David
Bootstrap 5 no longer requires jQuery which makes it easier to use with React. For example, here's the Bootstrap Collapse component using the React useState, useEffect hooks:
import { useState, useEffect } from React
import { Collapse } from bootstrap
function CollapseDemo() {
var [toggle, setToggle] = useState(false);
useEffect(() => {
var myCollapse = document.getElementById('collapseTarget')
var bsCollapse = new Collapse(myCollapse, {toggle: false})
toggle ? bsCollapse.show() : bsCollapse.hide()
})
return (
<div className="py-2">
<button className="btn btn-primary" onClick={() => setToggle(toggle => !toggle)}>
Toggle collapse
</button>
<div className="collapse" id="collapseTarget">
This is the collapsible content!
</div>
</div>
)
}
Demo
Figured I'd add an update here. With the updated version of React, not only do you not need vanilla code such as document.getElementById(), but you don't need refs either, or jQuery for that matter. You can simply import collapse like so:
import Collapse from 'react-bootstrap/Collapse'
The collapse transition can be accomplished very easily with this component, as shown in the docs. Here's the code pulled from the same:
const {useState} = React;
const Example = () => {
const [toggle, setToggle] = useState(false);
const toggleFunc = React.useCallback(() => setToggle(!toggle));
return (
<div>
<button onClick={toggleFunc}>Toggle Collapse</button>
<ReactBootstrap.Collapse in={toggle}>
<div>
Stuff to collapse
</div>
</ReactBootstrap.Collapse>
</div>
);
};
// Render it
ReactDOM.render(
<Example />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/react-bootstrap#next/dist/react-bootstrap.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div id="react"></div>
** Note: Obviously this code was modified to work with code snippets here on SO. If you're working in your local environment, use the code from the docs, which is even cleaner.
If you don't want to mess around with jQuery:
First, build a ref object for each of your collapsible elements; also build a function to toggle the .show CSS class to the corresponding element.
Then, use toggler function in a button onClick.
class Collapse extends React.Component {
constructor(props) {
super(props)
this.refs = {}
// build ref object with collapsible elements ids
this.setRef = (element) => {
this.refs[element.id] = element
}
// toggle "show" CSS class using plain JS
this.collapseRef = (id) => {
if (this.refs) this.refs[id].classList.toggle('show')
}
}
render() {
return (
<div>
<button
type="button"
onClick={() => this.collapseRef('content1')}
>
Collapse!
</button>
<div
className="collapse"
// Use the `ref` callback to store a reference to the collapsible DOM element
ref={this.setRef}
id="content1"
>
Collapsible content
</div>
</div>
)
}
}
I experienced this before. All you need to do is manually trigger events inside componentDidMount. You might also want to re-triggering the events in the callback of the setState.
Install the module with npm
npm install react-bootstrap bootstrap
And import in your component
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.min.js';
This work for me

Resources