How to append multiple DOM elements to an element created with reactjs? - javascript

I've just started work with reactjs and have not done much hands-on work with it. So far I'm able to create DOM elements through reactjs using JSXTransformer.js. The problem I'm getting is, when I try to create multiple elements within a DOM element, it replaces the old elements with the new ones.
That is, if I want to create div_B, div_C and div_D in mainDiv, it just adds div_D in the mainDiv because it is create last. But I want to append all three divs in the mainDiv.
The code I'm using is following:
var props = [];
function getEle(id) {
return document.getElementById(id);
}
function setProps(ele, Css, inner, id) {
props.element = ele;
props.CssClass = Css;
props.innerText = inner;
props.id = id;
return props;
}
function createElement(properties , element){
var CreateDiv = React.createClass({
render : function(){
return <div className = {this.props.elementProps.CssClass} id={this.props.elementProps.id}>{this.props.innerText}</div>;
}
});
React.render(<CreateDiv elementProps = {properties} />, element);
}
setProps("div", "divBClass", "", "div_B");
createElement(props, getEle("mainDiv"));
setProps("div", "divCClass", "", "div_C");
createElement(props, getEle("mainDiv"));
setProps("div", "divDClass", "", "div_D");
createElement(props, getEle("mainDiv"));
Is there anything wrong with that code?

You are still thinking about your code in an imperative manner. React is based on a declarative programming paradigm.
First, think about your whole application as a React component.
var App = React.createClass({
render: function() {
return (
<div>foo</div>
);
}
})
React.render(<App />, document.body);
Now, let's first render some paragraphs:
var App = React.createClass({
render: function() {
// construct array [0, 1, 2]
var values = [];
for (var i=0; i<this.props.noDivs; i++) {
values.push(i);
}
// return <p>0</p> <p>1</p> ...
return (
<div>
{values.map(function (value) {
return <p key={value}>Value {value}</p>;
})}
</div>
);
}
})
React.render(<App noDivs={3} />, document.body);
If JSX is too much, try to compile it to Javascript. Here is a live example. I'm passing the number of paragraphs as a prop.

Related

How can I modify an array in my class and get its new output outside of the class?

let arr = []
class Test extends React.Component {
handleConvertString = (event) => {
let str = this.inputRef.value;
let solutions = ['abb','klopp','lopp','hkhk','g','gh','a'] // Returnsolutions()
if (solutions.includes(str))
{
if (arr.includes(str))
{
alert("Answer already found");
}else{
arr.push(str)
}
}
}
}
Please note some code has been left out but the core issue I am having is outlined.
In this piece of code, I need to get the new contents of my arr(array) and return and access it outside my class.
I also need to be able to abstract it to a different file for usage after doing this.
How can I do this?
There are different ways to do this. Here you can follow the props method to solve your problem.
You have to pass values as props to other components to access the
value of the array outside of this component.
The below example will help you. I have done a few variations in your code to suit myself. The logic will remain the same and you can take help from it.
Note:
I am using a hardcoded value of str to check the result.
I am using displayResult variable and map function to just render the
result. In case the final array has more than one value then map
function will iterate and append each value of array in displayResult
variable and render it. You can avoid this as this is only written to show the result.
You can see clearly from the below code how we are passing values to another component (outside). Similarly, if you want to pass the value to another file you have to import that file and pass the value as props to the respective component defined in that file,
let displayResult = "";
function AnotherComponent(props) {
props.finalArr.map((num, index) => {
displayResult = displayResult.concat(num)
});
return (
<div>Value of final array is: {displayResult}</div>
)
}
class Test extends React.Component {
constructor(props) {
super(props)
this.state = {arr: []};
this.handleConvertString = this.handleConvertString.bind(this);
}
handleConvertString(event) {
let str = "abb";
let solutions = ['abb','klopp','lopp','hkhk','g','gh','a']
if (solutions.includes(str)) {
let finalArr = this.state.arr;
if (finalArr.includes(str)) {
alert("Answer already found");
}else{
finalArr.push(str)
this.setState({arr: finalArr})
}
}
}
render() {
return (
<div>
<button type="button"
onClick={this.handleConvertString}>
Click Me
</button>
<AnotherComponent finalArr={this.state.arr}/>
</div>
)
}
}
ReactDOM.render(<Test />, document.querySelector("#app"))
<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>
<div id="app"></div>

Setting custom props on a dynamically created React component

I'm refactoring some of my React code for ease of use in places where I can't use Babel directly (such as in short embedded JavaScript on pages). To assist with this I'm setting up a short function that builds the components and passes props to them. This code works just fine:
components.js:
import ResponsiveMenu from './components/responsive-menu';
window.setupMenu = (items, ele) => {
ReactDOM.render(<ResponsiveMenu items={items}/>, ele);
};
static-js.html:
<div id="menu"></div>
<script>
setupMenu({ items: [] }, document.getElementById('menu');
</script>
However, when I attempt to turn it into something more generic to handle more components like so:
components.js:
import ResponsiveMenu from './components/responsive-menu';
import AnotherComp from './components/another-comp';
window.setupComponent = (selector, name, props) => {
let eles;
if (typeof selector == 'string') {
eles = [];
let nl = document.querySelectorAll(selector), node;
for (let i = 0; node = nl[i]; i++) { eles.push(node); }
} else {
eles = $.toArray(selector); // A helper function that converts any value to an array.
}
return eles.map (
(ele) => {
let passProps = typeof props == 'function' ? props(ele) : props;
return ReactDOM.render(React.createElement(name, passProps), ele);
}
);
};
static-js.html:
<div id="menu"></div>
<script>
setupComponent('#menu', 'ResponsiveMenu', { items: [] });
</script>
I then get this error: Warning: Unknown prop "items" on <ResponsiveMenu> tag. Remove this prop from the element. For details, see (really unhelpful shortened link that SO doesn't want me posting)
Please help me understand why this works for the JSX version and not for the more manual version of creating the component.
When you pass string parameter to React.createElement, it will create native DOM element and there is no valid html DOM ResponsiveMenu.
You can store element into hash and store it into window variable.
Example:
// store component into window variable
window.components = {
ResponsiveMenu: ResponsiveMenu
}
//extract component from window variable by name
React.createElement(window.components[name], passProps)

How to make component instances in Reactjs

When I call ReactDom.render in init() only one g tag is created and all circles are created inside this g tag. but I want that on every ReactDom.render call a separate g tag must be created. My knowledge of components in React is that we can instantiate as many components as we want using React.createClass. How can I do this?
var Rect = React.createClass({
render: function() {
return (
React.createElement('circle', {
cx: this.props.cx,
cy: this.props.cy,
r: this.props.r,
fill: '#00ff00'
});
);
}
});
var globalArray = [];
var someFunc = function(cx, cy, r) {
var Factory = React.createClass({
render: function() {
var localArray = [];
for(var i = 0; i < 1; i++) {
localArray[i] = React.createElement(Rect, {
key: globalArray.length,
cx: cx,
cy: cy,
r: r
})
}
globalArray[globalArray.length] = localArray[0];
return (
React.createElement('g', {}, globalArray)
)
}
});
return Factory;
}
var randomNumber = function (x,y) {
return ~~(Math.floor(((Math.random()*x) + y )));
}
var obj = {
init: function() {
var element;
for(var i = 0; i < 100; i++) {
var cx = randomNumber(1200,40);
var cy = randomNumber(600,40);
var r = randomNumber(30,20);
element = someFunc(cx,cy,r);
ReactDOM.render(
React.createElement(element,{}),
document.getElementById('svg')
);
}
}
}
You'll probably find that you benefit from letting the structure of your code be defined by React components, rather than your own functions and global variables.
Generally, if you find yourself calling ReactDOM.render lots of times, then something has probably gone wrong.
Top Level Component
Rather than rendering 100 components into one element, define a top level component which is made up of 100 subcomponent instances, then render that once.
var Graphics = React.createClass({
// ...
});
ReactDOM.render(
React.createElement(Graphics, null),
document.getElementById('svg')
);
We'll create a Graphics component that we can use as a parent for the rest of the components. We only need to render this once.
State
Rather than storing your list of circles in a global array, use the state property on your new top level component. This way, whenever you update it, the component will re-render to reflect the changes.
getInitialState: function() {
// by default there are no circles (empty array)
return { circles: [] };
},
componentWillMount: function() {
var circles = [];
for(var i = 0; i < 100; i++) {
// rather than storing actual circles, we just store
// the props that are needed to create them
circles.push({
cx: randomNumber(1200, 40),
cy: randomNumber(600, 40),
r: randomNumber(30,20)
});
}
this.setState({ circles: circles });
}
getInitialState allows us to define default values for the properties on this.state and componentWillMount allows to run code just before the component is rendered in the DOM.
Render
Now that the Graphics component knows about the circles list, we have to describe the way it should be rendered.
render: function() {
var circles = this.state.circles.map(function(props) {
return React.createElement(Circle, props);
});
return React.createElement('g', null, circles);
}
This function uses map to create a Circle component using the props that we stored in this.state.circles.
Conclusion
React is most effective when you structure your components to live within one top level container component, like we've created here. Rather than ever doing any imperative actions (like looping and rendering components each time) you should look for declarative alternatives.
React wants you to tell it what you want, not how it should be done.
In this case, what you wanted was a component with a number of randomly sized and positioned circles inside it, but what you tried to do was explain to React how it should go about making that happen.
var Circle = React.createClass({
render: function() {
// ...
}
});
var Graphics = React.createClass({
getInitialState: function() {
// ...
},
componentWillMount: function() {
// ...
},
render: function() {
// ...
}
});
The resulting code should not only be shorter and easier to follow, but also easy to refactor. Without much work you could move configuration details — like the number of circles — out to being a prop of Graphics.

React JS: Explanation of this.props.items.map feature

I am using React JS for Rendering the HTML content. The issue is I am not able to understand particular section of code what it does.
If you can see a basic sample of a Todo List from the below link
http://facebook.github.io/react/
<script type='text/jsx'>
/** #jsx React.DOM */
var TodoList = React.createClass({
render: function(){
var createItem = function(itemText) {
return <li>{itemText}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
var TodoApp = React.createClass({
getInitialState: function(){
return {items:[], text: ''}
},
onChange: function(e)
{
this.setState({text: e.target.value});
},
handleSubmit: function(e)
{
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = ''
this.setState({items: nextItems, text: nextText});
},
render:function(){
return (
<div>
<h3>ToDo List</h3>
<TodoList items={this.state.items}/>
<form onSubmit={this.handleSubmit}>
<input type="text" onChange={this.onChange} value={this.state.text}/>
<button>Add #{this.state.items.length+1}</button>
</form>
</div>
)
}
});
React.render(<TodoApp />, document.getElementById('toDoListApp'));
</script>
I am basically not able to understand what map does and how create item parameters are working. Could anyone provide details on the same:
var TodoList = React.createClass({
render: function(){
var createItem = function(itemText) {
return <li>{itemText}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
Thanks,
Ankit
map is not a feature of React.js. You can call this function on any array you want. You should look at its documentation at MDN for that.
Basically, map is for converting an array to another array with modified items.
For example:
[1,2,3].map(function(item){
return item+1;
})
would return a new array like this: [2,3,4]
In your example, map is used to convert an array with items of type "string" to an array of React.DOM.li elements.
The autor of your example could also have done it like this
var TodoList = React.createClass({
render: function(){
return <ul>{this.createItems(this.props.items)}</ul>;
},
createItems: function(items){
var output = [];
for(var i = 0; i < items.length; i++) output.push(<li>{items[i]}</li>);
return output;
}
});
props is an object containing properties passed from a parent to a child component.
So props.items is the property named items which is an array.
props.item.map() maps the items arrary to an array of lis.
It will take this.props.items array, pass each item to the createItem function, and then return an array of the returned values of each call.
Specifically for that code, if you had this in this.props.items:
["Item 1 text", "Item 2 text", ..]
You'd get something like this from the map call:
["<li>Item 1 text</li>","<li>Item 2 text</li>",..]
this.props.items is an array and map return the new array according to callback function that provide as an first argument, it is quit easy to using ES6 and JSX.
<tr> { this.arr.map((obj, i) => <td key={i}> {obj.name} </td>) } </tr>
In our example it will return array of td's

How can I pass props/context to dynamic childrens in react?

I am using react, and I am trying to pass props/context to my dynamic childrens,
by dymamic childrens I mean childrens are render using
{this.props.children}
How can I pass to this children (In my code I know it's type) context/props?
In this jsbin there is an example that it dosen't work on dynamic childrens.
http://jsbin.com/puhilabike/1/edit?html,js,output
Though #WiredPrairie's answer is correct, the React.addons.cloneWithProps is deprecated as of React v0.13RC. The updated way to do this is to use React.cloneElement. An example:
renderedChildren = React.Children.map(this.props.children, function (child) {
return React.cloneElement(child, { parentValue: self.props.parentValue });
});
There's not a a great way to do this that is clear and passing all the properties of the parent isn't a great pattern and could lead to some very difficult to follow code if not done carefully (and with excellent documentation). If you have a subset of properties though, it's straightforward:
JsFiddle
Assuming you're using React with Addons, you can clone the children of a React component and set new property values on them. Here, the code just copies a property called parentValue into each child. It needs to create a clone of each element as the child element had already been created.
var Hello = React.createClass({
render: function() {
var self = this;
var renderedChildren = React.Children.map(this.props.children,
function(child) {
// create a copy that includes addtional property values
// as needed
return React.addons.cloneWithProps(child,
{ parentValue: self.props.parentValue } );
});
return (<div>
{ renderedChildren }
</div>)
;
}
});
var SimpleChild = React.createClass({
render: function() {
return <div>Simple { this.props.id }, from parent={ this.props.parentValue }</div>
}
});
React.render((<Hello parentValue="fromParent">
<SimpleChild id="1" />
<SimpleChild id="2" />
</Hello>), document.body);
Produces:
Simple 1, from parent=fromParent
Simple 2, from parent=fromParent
Spreading props on DOM elements
https://github.com/vasanthk/react-bits/blob/master/anti-patterns/07.spreading-props-dom.md
When we spread props we run into the risk of adding unknown HTML
attributes, which is a bad practice.
const Sample = () => (<Spread flag={true} domProps={{className: "content"}}/>);
const Spread = (props) => (<div {...props.domProps}>Test</div>);

Categories

Resources