jquery styling not working in react component - javascript

jQuery seems to be working fine in react component however, when I try to apply styling using jquery in react component its not working. In the below code console.log(eachVisitedTopic) within each loop is returning proper result as expected.
topicsVisited(arr){
$(function() {
$.each(arr, function(key, eachVisitedTopic) {
console.log(eachVisitedTopic);
$('.single-topic[data-topic-id="' + eachVisitedTopic + '"]').css({
'background-color': 'red'
});
});
});
};
Markup
import {React, ReactDOM} from '../../../../build/react';
export default class SingleTopicBox extends React.Component {
render() {
return (
<div>
<div className="col-sm-2">
<div className="single-topic" data-topic-id={this.props.topicID} onClick={() => this.props.onClick(this.props.topicID)}>
{this.props.label}
{this.props.topicID}
</div>
</div>
</div>
);
}
};

React should handle all the render, it checks the dirty dom and render only things that changed.
You can achieve what you want, just use a react state.
When you trigger a setState change react will look into the DOM and find what has changed and then render it.
Ref: https://facebook.github.io/react/docs/component-api.html#setstate
constructor() {
super();
this.state = {
bgDisplayColor: "blue"
};
}
Then you can do something like this in yout component:
$('.single-topic[data-topic-id="' + eachVisitedTopic + '"]').css({
'background-color': this.state.bgDisplayColor
});
And to update it you simply use:
this.setState({bgDisplayColor: "red"});
EDIT
To workaround the undefined variable error, you have to store the scope of "this" inside your function and use instead of "this", because inside the jquery .css "this" refers to Jquery and not the "this" scope of your actual class.
Example:
topicsVisited(arr){
var self = this;
$(function(){
$.each(arr, function(key, eachVisitedTopic){
console.log(eachVisitedTopic);
//self here is the global scope of your class
//Inside jQuery.css this refers to Jquery and not to your class.
$('.single-topic[data-topic-id="' + eachVisitedTopic + '"]').css({
'background-color': self.state.bgDisplayColor
});
});
});
});
};

Try it to by putting all jQuery code inside the componentDidMount
E.g :
componentDidMount() {
//Your jQuery function goes here
}

Related

Javascript Web-Components - Add function to the shadow-DOM

I'm working on new web-components for my site, which work fine just with html/css. However, I was not able to add some kind of javascript functionality to my shadow DOM.
In my case, it's about a button inside the component, which should trigger a function handling the event. Nevertheless, I always get an error that the function is not defined. I know that the shadow DOM should protect the inside but I do not know how to implement my js properly. Hopefully you can help :)
class InfoSmall extends HTMLElement {
// attributes
constructor() {
super();
// not important
}
// component attributes
static get observedAttributes() {
return [''];
}
// attribute change
attributeChangedCallback(property, oldValue, newValue) {
if (oldValue == newValue) return;
this[ property ] = newValue;
};
connectedCallback() {
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<div class="container>
<button id="copy"></button>
</div>`
shadow.querySelector('#copy').addEventListener("click", function () {
// functionality
});
}
}
I also tried the onclick-attribute but it was the same for me: no function defined. Or I also tried writing the script inside the innerHTML with an HTML-tag...
You are creating invalid HTML because you are missing a double quote on class="container>
Your code can be condensed to:
<script>
customElements.define("my-element", class extends HTMLElement {
constructor() {
super() // sets AND returns 'this' scope
.attachShadow({mode: 'open'}) // sets AND returns this.shadowRoot
.innerHTML = `<div class="container">
<button>Click Me!</button>
</div>`;
this.shadowRoot
.querySelector('button')
.onclick = () => {
alert("YEAH!")
};
}
});
</script>
<my-element></my-element>

How to bind a method to `this` inside a constructor of a class (ES6)?

So I have the following simple ES6 plugin that can be used to change the background of an element (created for demo purpose) :
src/index.js
class changeBGColor {
constructor({ targetBG , triggerEl }) {
this.changeBGElem = document.getElementById(targetBG);
this.triggerElem = document.getElementById(triggerEl);
this.addEventListeners();
//this.onClick = this.onClick.bind(this);
//this.onClick.bind(this);
}
addEventListeners() {
this.triggerElem.addEventListener( 'click' , this.onClick.bind(this) );
}
changeBG() {
const bgChangeBgElem = this.changeBGElem;
bgChangeBgElem.classList.toggle('myclassName');
}
onClick(ev) {
console.log(this);
if( ev.target.hasAttribute('data-changeBG') ) {
this.changeBG();
}
}
}
export default changeBGColor;
And I am using my plugin in my main.js file like so:
src/main.js
import changeBGColor from './index.js';
new changeBGColor({
targetBG : 'changeBGOne',
triggerEl : 'triggerBGOne'
});
new changeBGColor({
targetBG : 'changeBGTwo',
triggerEl : 'triggerBGTwo'
});
Now the problem i have is inside addEventListeners method, I have the following code:
this.triggerElem.addEventListener( 'click' , this.onClick.bind(this) );
As you can see I use bind() to bind the scope of this to the class changeBGColor , but I don't want to do this in this manner.
I want something like:
this.onClick = this.onClick.bind(this); // i don't really know what this does, saw it in another plugin, but somehow , this does't work for me.
The problem is the above doesn't work either. I copied it above solution from a GitHub repository, so my question is why does the above solution not work for me and more importantly what is it really doing because this.onClick = this.onClick.bind(this); doesn't make sense to me at all, can somebody break this down for me, please.
P.S. I don't want to use arrow functions to circumvent this problem, that works fine, I know.
Here is a working example of the plugin: JSfiddle.

How to call method inside the same class in ReactJS?

I want to call the method inside the same class. For example, when I click a button, it will trigger the method handleLoginBtnClicked(). I expect it will call the method checkInputValidation() in the same class. What is the proper way to do this?
export default class LoginCard extends React.Component {
//If I click a button, this method will be called.
handleLoginBtnClicked() {
this.checkInputValidation();
}
checkInputValidation() {
alert("clicked");
}
...
...
...
render() {
...
<LoginBtn onClick={this.handleLoginBtnClicked}/>
...
}
}
Error Message:
Uncaught TypeError: this.checkInputValidation is not a function
You will need to bind those functions to the context of the component. Inside constructor you will need to do this:
export default class LoginCard extends React.Component {
constructor(props) {
super(props);
this.handleLoginBtnClicked = this.handleLoginBtnClicked.bind(this);
this.checkInputValidation = this.checkInputValidation.bind(this);
}
//This is the method handleLoginBtnClicked
handleLoginBtnClicked() {
...
}
//This is the method checkInputValidation
checkInputValidation() {
...
}
...
..
.
}
Where are you binding the handleLoginBtnClicked? You may be losing the functions context and losing the meaning of the special variable this. React will handle and trigger the onClick event, calling the function from a different context which is why it has been lost.
You should use the following syntax to create a new bound function to add as the event listener for the onClick event. This will ensure that handleLoginBtnClicked's context is not lost.
<element onClick={this.handleLoginBtnClicked.bind(this)}>

Get wrapper DOM element attributes from a method in React

I have a link in a React component:
<a href="#goals-tab" className={ this.setTabStyle()}>Goals</a>
Now, inside setTabStyle method, can I access attributes of the a element, like href without explicitly passing it to the method as a parameter?
If you use a ref, then your component renders DOM without the styles, and then applies the new styles. So the user will notice the change of styles.
I would advise to pass link as a parameter to setTabStyle(link), or make the link another prop of your component:
var Component = React.createClass({
handleClick: function (e) {
console.log(e.currentTarget.getAttribute('href'));
},
setTabStyle: function () {
if (this.props.link == this.props.activelink) {
return myActiveLinkStyle
} else {
return myInactiveLinkStyle
}
},
render: function() {
return <a href={this.props.link} style={this.setTabStyle()} onClick={this.handleClick}>Click</a>;
}
});
That way, you get the right style from the initial load..

How to append to dom in React?

Here's a js fiddle showing the question in action.
In the render function of a component, I render a div with a class .blah. In the componentDidMount function of the same component, I was expecting to be able to select the class .blah and append to it like this (since the component had mounted)
$('.blah').append("<h2>Appended to Blah</h2>");
However, the appended content does not show up. I also tried (shown also in the fiddle) to append in the same way but from a parent component into a subcomponent, with the same result, and also from the subcomponent into the space of the parent component with the same result. My logic for attempting the latter was that one could be more sure that the dom element had been rendered.
At the same time, I was able (in the componentDidMount function) to getDOMNode and append to that
var domnode = this.getDOMNode();
$(domnode).append("<h2>Yeah!</h2>")
yet reasons to do with CSS styling I wished to be able to append to a div with a class that I know. Also, since according to the docs getDOMNode is deprecated, and it's not possible to use the replacement to getDOMNode to do the same thing
var reactfindDomNode = React.findDOMNode();
$(reactfindDomNode).append("<h2>doesn't work :(</h2>");
I don't think getDOMNode or findDOMNode is the correct way to do what I'm trying to do.
Question: Is it possible to append to a specific id or class in React? What approach should I use to accomplish what I'm trying to do (getDOMNode even though it's deprecated?)
var Hello = React.createClass({
componentDidMount: function(){
$('.blah').append("<h2>Appended to Blah</h2>");
$('.pokey').append("<h2>Can I append into sub component?</h2>");
var domnode = this.getDOMNode();
$(domnode).append("<h2>appended to domnode but it's actually deprecated so what do I use instead?</h2>")
var reactfindDomNode = React.findDOMNode();
$(reactfindDomNode).append("<h2>can't append to reactfindDomNode</h2>");
},
render: function() {
return (
<div class='blah'>Hi, why is the h2 not being appended here?
<SubComponent/>
</div>
)
}
});
var SubComponent = React.createClass({
componentDidMount: function(){
$('.blah').append("<h2>append to div in parent?</h2>");
},
render: function(){
return(
<div class='pokey'> Hi from Pokey, the h2 from Parent component is not appended here either?
</div>
)
}
})
React.render(<Hello name="World" />, document.getElementById('container'));
In JSX, you have to use className, not class. The console should show a warning about this.
Fixed example: https://jsfiddle.net/69z2wepo/9974/
You are using React.findDOMNode incorrectly. You have to pass a React component to it, e.g.
var node = React.findDOMNode(this);
would return the DOM node of the component itself.
However, as already mentioned, you really should avoid mutating the DOM outside React. The whole point is to describe the UI once based on the state and the props of the component. Then change the state or props to rerender the component.
Avoid using jQuery inside react, as it becomes a bit of an antipattern. I do use it a bit myself, but only for lookups/reads that are too complicated or near impossible with just react components.
Anyways, to solve your problem, can just leverage a state object:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://fb.me/react-0.13.3.js"></script>
</head>
<body>
<div id='container'></div>
<script>
'use strict';
var Hello = React.createClass({
displayName: 'Hello',
componentDidMount: function componentDidMount() {
this.setState({
blah: ['Append to blah'],
pokey: ['pokey from parent']
});
},
getInitialState: function () {
return {
blah: [],
pokey: []
};
},
appendBlah: function appendBlah(blah) {
var blahs = this.state.blah;
blahs.push(blah);
this.setState({ blah: blahs });
},
render: function render() {
var blahs = this.state.blah.map(function (b) {
return '<h2>' + b + '</h2>';
}).join('');
return React.createElement(
'div',
{ 'class': 'blah' },
{ blahs: blahs },
React.createElement(SubComponent, { pokeys: this.state.pokey, parent: this })
);
}
});
var SubComponent = React.createClass({
displayName: 'SubComponent',
componentDidMount: function componentDidMount() {
this.props.parent.appendBlah('append to div in parent?');
},
render: function render() {
var pokeys = this.props.pokeys.map(function (p) {
return '<h2>' + p + '</h2>';
}).join('');
return React.createElement(
'div',
{ 'class': 'pokey' },
{ pokeys: pokeys }
);
}
});
React.render(React.createElement(Hello, { name: 'World' }), document.getElementById('container'));
</script>
</body>
</html>
Sorry for JSX conversion, but was just easier for me to test without setting up grunt :).
Anyways, what i'm doing is leveraging the state property. When you call setState, render() is invoked again. I then leverage props to pass data down to the sub component.
Here's a version of your JSFiddle with the fewest changes I could make: JSFiddle
agmcleod's advice is right -- avoid JQuery. I would add, avoid JQuery thinking, which took me a while to figure out. In React, the render method should render what you want to see based on the state of the component. Don't manipulate the DOM after the fact, manipulate the state. When you change the state, the component will be re-rendered and you'll see the change.
Set the initial state (we haven't appended anything).
getInitialState: function () {
return {
appended: false
};
},
Change the state (we want to append)
componentDidMount: function () {
this.setState({
appended: true
});
// ...
}
Now the render function can show the extra text or not based on the state:
render: function () {
if (this.state.appended) {
appendedH2 = <h2>Appended to Blah</h2>;
} else {
appendedH2 = "";
}
return (
<div class='blah'>Hi, why isn't the h2 being appended here? {appendedH2}
<SubComponent appended={true}/> </div>
)
}

Categories