Get wrapper DOM element attributes from a method in React - javascript

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..

Related

vue dialog doesn't close/hide

I can't close or hide my vue-dialog and I don't know why. This is my js file from which I call the dialog:
<ItemChooserDialog v-model="showItemChooser" #onItemSelected="onStockSelected" />
export default {
data() {
return {
showItemChooser: false,
}
}),
methods: {
chooseItem(context) {
var self = this;
self.showItemChooser = true;
},
onStockSelected(result) {
var self = this;
let id = result.id;
let name = result.name;
self.showItemChooser = false;
},
}
and this is my Dialog:
<template>
<v-dialog v-model="show" max-width="600">
...
</v-dialog>
</template>
computed: {
show: {
get () {
return this.value
},
set (value) {
this.$emit('input', value)
}
}
},
props: {
value: Boolean
},
methods: {
rowClick{
this.$emit('onItemSelected', clicked_item);
}
}
If I choose an item in the dialog the callback-method "onStockSelected" of the js file runs and self.showItemChooser is set to false but the dialog is still visible.
You don't have any code that actually hides the dialog. The most common way to conditionally hide/show an element is to use the v-if directive.
On your dialog element:
<template>
<v-dialog v-if="value" v-model="show" max-width="600">
...
</v-dialog>
</template>
Check this codesandbox I made: https://codesandbox.io/s/stack-70146776-6tczf?file=/src/components/ItemChooserDialog.vue
I can see you may took as example one of my previous answers. In this case, since you're using the prop value of your custom dialog component to handle the v-dialog v-model. You can close the dialog in two ways.
Closing the dialog from the parent component by setting up your showItemChooser to false. As you're doing on your example. Which I think it's not working because you declared your rowClick method without parentheses in your child component.
Also on my example I decided to use kebab-case on my event name but that just a convention from eslint. Everything else looks alright.
onStockSelected(result) {
var self = this;
let id = result.id;
let name = result.name;
self.showItemChooser = false;
},
Closing the dialog from the child component. Since you're using the computer property show on the v-model of your v-dialog. You can close the dialog by simply setting the show value to false.
closeFromChild() {
this.show = false
}
Last but not least, v-dialog's by default close themselves if you click outside the dialog. If you want to avoid this functionality you can set the persistent prop to your dialog as I did on my example.

ReactJS - What is the standard method of initializing jquery plugin in a react class?

I am using this plugin - bootstrap-dropselect
I have written initDropSelect function to initialize this plugin but I am not sure where to call this function as I would like to append some html to the DOM as soon as route is loaded. I am getting data from two different ajax calls. That data has to be compared and manipulated to append that html to the DOM(Code below 'Append to DOM' comment).
let UserPanel = React.createClass({
mixins: [LinkedStateMixin],
getStateFromStores: function() {
var users = UserStore.getAll();
// Some more code
return {
users: users
// Other properties
};
}
componentDidMount: function() {
UserStore.addChangeListener(this._onChange);
},
_onChange: function() {
this.setState(this.getStateFromStores());
},
initDropSelect: function() {
var _self = this;
var dropSelect = $('#dropselect-demo1').dropselect({
filter: {
show: true,
placeholder: 'Search for an item'
},
multiselect: true,
onselect: function(e, item) {
},
onunselect: function(e, item) {
},
onclear: function(e) {
}
});
// Append to DOM
if(this.state.tagsList.length > 0) {
if(this.state.newLoan.data.tags.length > 0) {
// Getting data from two different resources
}
}
}
});
Please help me in deciding where to call initDropSelect to manipulate data from multiple async requests and append that data to DOM.
P.S. I am using react router so there are two scenarios. First I may come to this route from other route or I can straightaway reload the current page.
Thanks in advance.
One way is for your render method to return something like a <div /> that you will then use to mount the jQuery component into. Once mounted, this will point to the DOM element that you would normally pass to jQuery.
Since jQuery will be handling the rendering, you then want to always return false from shouldComponentUpdate(). This will prevent React from thrashing your jQuery component.
You can then use componentDidMount() to initialize your jQuery component and componentWillReceiveProps() to update/re-render it when new data is passed in.

React: Unable to access child props in parent's event handler

I'm using React to create a UI and I have a parent component and a child component, something along these lines:
// Child component
var ListItem = React.createClass({
render: function() {
var link_details = (
<div>
Start Date: {this.props.my_data.start_date}<br/>
End Date: {this.props.my_data.end_date}<br/>
</div>
);
return (
<li>
<a onClick={this.props.clickHandler}>
{ this.props.my_data.name }
</a>
{link_details}
</li>
)
}
});
// Parent component
var Sidebar = React.createClass({
getInitialState: function() {
return {
my_data: [],
};
},
handleListItemClick: function(e){
console.log(e.target);
console.log(e.target.props);
},
render: function() {
var myLinks = this.state.my_data.map(function(mylink) {
return (
<ListItem key={mylink.id} my_data={mylink} clickHandler={this.handleListItemClick} />
);
}.bind(this));
return (
<div>
<ul className="nav nav-sidebar">
{ myLinks }
</ul>
</div>)
}
});
I want the click event on the child to trigger the parent's handler so that the parent can update its state based on what was clicked in the child. While the code I have above works, and the parent's handler is called, I am unable to access any of the child component's props. I'm not sure if that's by design and I should pass data from the child to the parent in a different way, or if I'm doing something wrong. I'm still very new to React, so any advice is appreciated. Thanks!
You can not do that but you can pass data from child to parent via callback
<li>
<a onClick={this.props.clickHandler.bind(null,this.props.my_data.name)}>
{ this.props.my_data.name }
</a>
{link_details}
</li>
or using arrow function if you are using es6
<li>
<a onClick={() => this.props.clickHandler(this.props.my_data.name)}>
{ this.props.my_data.name }
</a>
{link_details}
</li>
Edit
Why passing null?
Things to remember:
Automatic binding methods to 'this' happens when your component mounts.
There are two conditions
1.Calling a callback passed from parent component to a child component
When we directly pass functions (e.g. this.clickHandler) to a child component without worrying about the value of 'this' when the function is actually called.
React then the replaces the standard Function.prototype.bind method with its own function to help stop you from doing anything silly (like trying to change the already-bound value of 'this'), so you instead have to pass 'null' to say "I understand this will only alter the arguments".
2.Calling a function defined within same component
React does not do this for function calls within the same component
Rules for binding
If you want to set the first argument by calling .bind on a function...
passed in via props, pass null as the first argument e.g.
this.props.funcName.bind(null, "args")
taken from 'this', pass 'this' as the first argument e.g.
this.funcName.bind(this, "args")
You can do so:
var ListItem = React.createClass({
clickItem: function (e) {
this.props.clickHandler(e, this.props.my_data); // now you can pass any data to parent
},
render: function() {
var link_details = (
<div>
Start Date: {this.props.my_data.start_date}<br/>
End Date: {this.props.my_data.end_date}<br/>
</div>
);
return (
<li>
<a onClick={this.clickItem}>
{ this.props.my_data.name }
</a>
{link_details}
</li>
)
}
});
I took a look at the answer on Pass props to parent component in React.js and came up with the following:
// Parent component
var Sidebar = React.createClass({
getInitialState: function() {
return {
my_data: [],
};
},
handleListItemClick: function(data_passed, e){
console.log(data_passed);
},
render: function() {
var myLinks = this.state.my_data.map(function(mylink) {
return (
<ListItem key={mylink.id} my_data={mylink} clickHandler={this.handleListItemClick.bind(null, mylink.id)} />
);
}.bind(this));
return (
<div>
<ul className="nav nav-sidebar">
{ myLinks }
</ul>
</div>)
}
});
This does seem to work- I'd be interested in seeing other solutions and which one is the "best" and why.

jquery styling not working in react component

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
}

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