React render component multiple times in container with different props? - javascript

I am trying to create a reusable "tag" React component, so that users can create tags onclick and see that information displayed in the DOM.
Here's the module:
module.exports = React.createClass({
render: function() {
return (
<div className="language chip" data-lang={this.props.language} data-lang-level={this.props.level}>
{this.props.language} ({this.props.level})
<i className="material-icons">close</i>
</div>
);
}
});
And the onclick call:
var addLanguage = $('a#add-language');
addLanguage.click(function() {
var languageLearning = $('#language-learning');
var levelLearning = $('#language-level');
if (languageLearning != null && levelLearning != null) {
ReactDOM.render(
<LanguageChip
language={languageLearning.val()}
level={levelLearning.val()}
/>,
document.getElementById('language-chips')
);
languageLearning.select2('val', '');
levelLearning.select2('val', '');
}
})
I didn't realise that when using React.DOM, "Any existing DOM elements inside are replaced when first called." This means when adding a second chip, the first is removed. I want users to be able to have multiple chips.
How can I do this?

I don't know if you've got a good reason to not add the form used to create a tag on your component, but it would be much simpler if you could.
Then you just have to add your tags on an array and display them with your LanguageChip component.
I've made an example here: https://jsfiddle.net/snahedis/69z2wepo/28193/
I don't know what's your level of understanding of React so if something isn't clear let me know :)
Edit: the same example inside a preexistent form:
https://jsfiddle.net/snahedis/69z2wepo/28217/

You need to use array to store multiple chips data. Take a look to this simplified example: http://output.jsbin.com/seficu
var LanguageChips = React.createClass({
render: function() {
return (
<div>
{
(this.props.chipsArray).map(function(chip, index) {
return <LanguageChip
key={index}
language={chip.languageLearning}
level={chip.levelLearning}
/>
})
}
</div>
);
}
});
var LanguageChip = React.createClass({
render: function() {
return (
<div className="language chip" data-lang={this.props.language} data-lang-level={this.props.level}>
{this.props.language} ({this.props.level})
<i className="material-icons"></i>
</div>
);
}
});
var chipsArray = [];
document.getElementById('add-language').addEventListener("click", function() {
var languageLearning = 'test1';
var levelLearning = 'test2';
if (languageLearning != null && levelLearning != null) {
chipsArray.push({
languageLearning: languageLearning,
levelLearning: levelLearning
});
ReactDOM.render(
<LanguageChips chipsArray={chipsArray} />,
document.getElementById('language-chips')
);
}
})

Related

bind() in render method gives me a warning?

My code is working properly but it gives me a warning
Warning: bind(): You are binding a component method to the component. React does this for you automatically in a high-performance way, so you can safely remove this call.
I also use onClick={this.myFun.bind(null,"myvalue")} as describe in the Why does React warn me against binding a component method to the object?
Still it give me warning.
My Code :
var MyClass = React.createClass({
myFun : function (value){
console.log(value);
},
render: function () {
var that = this;
var card = this.props.data.map(function (val,key) {
return (
<p onClick={that.myFun.bind(null,val)}> Click Me</p>
);
});
return (
<div>
{card}
</div>
);
}
});
There are a few things wrong with your code
render: function () {
var this = that;
should be
render: function () {
var that = this;
return ( {card}) will give an error that you may have returned undefined or null or an Object and hence you need to wrap it inside a div like <div>{card}</div> to return a React Element.
You component name must begin with a upper case Character. See the explanation here: React - Adding component after AJAX to view
See the working demo
var MyClass = React.createClass({
myFun : function (value){
console.log(value);
},
render: function () {
var that = this;
var card = this.props.data.map(function (val,key) {
return (
<p key={key} onClick={that.myFun.bind(null,val)}> Click Me</p>
);
});
return (
<div>{card}</div>
);
}
});
ReactDOM.render(<MyClass data={['1', '2']}/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="app"></div>
NOTE: React.createClass is deprecated and will be removed in v16.0
and hence you should write your component by extending
React.Component syntax
The only you missed is assign that to this
var myClass = React.createClass({
myFun : function (value){
console.log(value);
},
render: function () {
var that = this;
var card = this.props.data.map(function (val,key) {
return <p onClick={that.myFun.bind(null,val)}> Click Me</p>;
});
return <div>{card}</div>;
}
});
The best way to do this is to extract it into a sub-component and pass the params as props. This way you won't be creating a new function on every render, and will prevent unnecessary re-renders (More info here jsx-no-bind):
var Item = React.createClass({
handleClick: function() {
this.props.onItemClick(this.props.val);
},
render: function() {
return (
<p onClick={this.handleClick}>Click Me</p>
);
}
});
var MyClass = React.createClass({
myFun: function (value){
console.log(value);
},
render: function () {
var that = this;
var card = this.props.data.map(function (val, key) {
return (
<Item onItemClick={that.myFun} val={val} />
);
});
return (
<div>{card}</div>
);
}
});
ReactDOM.render(
<MyClass data={['a', 'b', 'c']} />,
document.getElementById('test')
);
<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="test"></div>
Also you should look into using or learning es6, which will clean up your code considerably.

ReactJS onClick not working - nothing happends in the browser and in the system console

I've read a lot of threads about this problem, but still I can't fix it.
/** #jsx React.DOM */
var React = require('react/addons');
var SegmentComponent = React.createClass({
handleThatEvent: function (e)
{
console.log('clicked');
},
render: function ()
{
const styles = {
left: this.props.leftPercent + '%',
width: this.props.widthPercent + '%'
};
if (this.props.color !== undefined)
{
styles.backgroundColor = this.props.color;
}
return (
<span onClick={this.handleThatEvent()} style={styles} className="track-notation"></span>
)
}
});
module.exports = SegmentComponent;
When I click on span nothing happends. I'm new in ReactJS, so maybe I missed an obvious thing.
There is not a "clicked" text in the web browser console and in the system console (Terminal).
EDIT:
I tried with
onClick={this.handleThatEvent.bind(this)}
and
{() => this.handleThatEvent()}
and is still nothing.
Also in HTML there isn't onClick attribute in span element:
<span style="left:10%;width:10%;" class="track-notation" data-reactid=".rru3h8zkm2.1:0.0.2.0"></span>
onClick={this.handleThatEvent()}
replace with
onClick={this.handleThatEvent}
Careful you've passed this.handleThatEvent() into onClick event, and executed it there, you need to pass only the function this.handleThatEvent on onClick will execute it... see:
const SegmentComponent = React.createClass({
handleThatEvent: function (e) {
console.log('clicked');
},
render: function () {
const styles = {
left : this.props.leftPercent + '%',
width : this.props.widthPercent + '%'
};
if (this.props.color !== undefined) {
styles.backgroundColor = this.props.color;
}
return (
<span
onClick={this.handleThatEvent}
style={styles}
className="track-notation"
>Click me!
</span>
)
}
});
ReactDOM.render(
<SegmentComponent />,
document.getElementById('root')
)
<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="root"></div>
that's the problem:
onClick={this.handleThatEvent()}
should be like that:
onClick={this.handleThatEvent.bind(this)}
or
onClick={() => this.handleThatEvent()}

how to render child component of only one generated child in react

In a React app, I have a component to display the results of a search called ResultsContainer. I am generating children by mapping a prop that has been passed down from a parent and creating a list element from each item in the array. Each of these children has a component that I (DetailsContainer) want to render when a button is clicked, and ONLY for the child that had the button clicked. As of now, the component renders for all of these children. I am not sure how to target the individual child so that the component only renders for that specific one.
ResultsContainer:
var React = require('react');
var DetailsContainer = require('./DetailsContainer.jsx');
var ResultsContainer = React.createClass ({
render: function () {
var queryType = this.props.queryType;
var details = this.props.details;
var that = this;
if (this.props.results == null) {
var searchResult = "Enter search terms"
} else {
var searchResult = this.props.results.map(function(result, index){
//if artist search
if (queryType == "artist") {
if (result.type == "artist") {
return <li className="collection-item" key={index}> {result.title} <button onClick={that.props.handleDetailClick}>Get details</button> <DetailsContainer details={details} /> </li> ;
}
}
});
};
return (
<div className="collection">
{searchResult}
</div>
);
},
});
module.exports = ResultsContainer;
When the button is clicked, I want to render the DetailsContainer component for that child ONLY, not all of them. How could I do this?
DetailsContainer:
var React = require('react');
var DetailsContainer = React.createClass({
render: function(){
if (this.props.details !== null) {
var detailsDisplay = "deets"
}
return (
<div className="detailsDisplay">
{detailsDisplay}
</div>
);
},
});
module.exports = DetailsContainer;
Here is handleDetailClick a function being passed down from the parent as a prop:
handleDetailClick: function() {
this.setState({details: "details"});
},
Also, in ResultsContainer, I could not figure out how to use .bind() to be able to use this.props.handleDetailClick from inside the callback of the .map, so i just created a variable "that" and set it equal to "this" outside the function. Is this bad practice? What is the proper way to do it using .bind()? Thanks so much.
First off map has an option to pass this. map(function(item){}, this); You have at least two options to limit the list. One is to use state like so;
render() {
let useList;
if (this.state.oneSelected)
useList = this.props.list[this.state.oneSelected];
else
useList = this.props.list;
let useListing = useList.map(function(item){}, this);
return ({useListing})
}
The other option is to pass the selected item to the parent this.props.selectOne(item.id) and have the parent return just the one item as a list.

Find all components with type in reactjs

I am having trouble finding components by type using scryRenderedComponentsWithType
code:
describe('Layout', function() {
it('test', function(done) {
var Wrapper = React.createClass({
render: function() {
return <div className="testWrapper">Hello <span>Jim<div>hi<ul><li><div><span></span></div></li></ul></div></span></div>;
}
});
var TestWrap = React.createClass({
render() {
return (
<div>
<p>Test this </p>
<Wrapper />
</div>
);
}
})
var renderedTree = TestUtils.renderIntoDocument(<TestWrap />);
var renderedMyComponent = TestUtils.scryRenderedComponentsWithType(renderedTree, 'Wrapper');
console.log(renderedMyComponent.length);
done();
});
});
the output of this test returns an array length of 0. I think I maybe using the function incorrectly but I am not sure where I went wrong.
TestUtils.scryRenderedComponentsWithType(renderedTree, {function})
This requires that the second argument be a function, not a string.
Hence, your variable Wrapper will work, but not the string "Wrapper".

Custom image toggle button in ReactJS

I have this ReactJS code to show a custom image button that toggles between 2 different images for ON and OFF state. Is there a simpler way to do this? I was hoping CSS might be less lines of code, but wasn't able to find a simple example.
The code below passes state up from <MyIconButton> to <MyPartyCatButton> then to <MyHomeView>. My app will have 4 of these custom buttons on the home screen, which is why I factored out <MyIconButton>.
btw - this is for a mobile App and I read (and noticed this myself) it's really slow using checkboxes on mobile browsers; that's why I chose to try this without using checkboxes.
ReactJS code
var MyIconButton = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
console.log("INSIDE: MyIconButton handleSubmit");
// Change button's state ON/OFF,
// then sends state up the food chain via
// this.props.updateFilter( b_buttonOn ).
var b_buttonOn = false;
if (this.props.pressed === true) {
b_buttonOn = false;
}
else {
b_buttonOn = true;
}
// updateFilter is a 'pointer' to a method in the calling React component.
this.props.updateFilter( b_buttonOn );
},
render: function() {
// Show On or Off image.
// ** I could use ? : inside the JSX/HTML but prefer long form to make it explicitly obvious.
var buttonImg = "";
if (this.props.pressed === true) {
buttonImg = this.props.onpic;
}
else {
buttonImg = this.props.offpic;
}
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type="image" src={buttonImg}></input>
</form>
</div>
);
}
});
// <MyPartyCatButton> Doesn't have it's own state,
// passes state of <MyIconButton>
// straight through to <MyHomeView>.
var MyPartyCatButton = React.createClass({
render: function() {
return (
<MyIconButton pressed={this.props.pressed} updateFilter={this.props.updateFilter} onpic="static/images/icon1.jpeg" offpic="static/images/off-icon.jpg"/>
);
}
});
//
// Main App view
var MyHomeView = React.createClass({
getInitialState: function() {
// This is where I'll eventually get data from the server.
return {
b_MyPartyCat: true
};
},
updatePartyCategory: function(value) {
// Eventually will write value to the server.
this.setState( {b_MyPartyCat: value} );
console.log("INSIDE: MyHomeView() updatePartyCategory() " + this.state.b_MyPartyCat );
},
render: function() {
return (
<div>
<MyPartyCatButton pressed={this.state.b_MyPartyCat} updateFilter={this.updatePartyCategory}/>
</div>
// Eventually will have 3 other categories i.e. Books, Skateboards, Trees !
);
}
});
if you update the coponent 'pressed' prop dynamically (like you did), simply
var MyIconButton= React.createClass({
render: function(){
var pic= this.props.pressed? this.props.onpic : this.props.offpic
return <img
src={pic}
onClick={this.props.tuggleSelection} //updateFilter is wierd name
/>
}
})
(EDIT: this way, on MyPartyCatButton component, you can pass function to handle 'tuggleSelection' event. event function argument is an event object, but you have the button state allready in the wrapper state (the old one, so you should invert it). your code will be something like that:
render: function(){
return <MyIconButton pressed={this.state.PartyCatPressed} tuggleSelection={this.updatePartyCategory} />
}
updatePartyCategory: function(e){
this.setState(
{PartyCatPressed: !this.state.PartyCatPressed} //this invert PartyCatPressed value
);
console.log("INSIDE: MyHomeView() updatePartyCategory() " + this.state.b_MyPartyCat )
}
)
but if you don't, use prop for defult value:
var MyIconButton= React.createClass({
getInitialState: function(){
return {pressed: this.props.defultPressed}
},
handleClick: function(){
this.setState({pressed: !this.state.pressed})
},
render: function(){
var pic= this.state.pressed? this.props.onpic : this.props.offpic
return <img
src={pic}
onClick={this.handleClick}
/>
}
})

Categories

Resources