ReactDOM dynamically rendering components from array - javascript

I want my RenderDOM to render one component for each object found in an array. I'm building the render with JSX, and my code is as follows:
ReactDOM.render((
<div className="container container-collapsed">
<Actions Units={aUnits} />
<div className="units-wrapper">
{
aUnits.forEach(u=> {
return <Unit unit={u} />
})
}
</div>
</div>
), document.getElementById("root"));
I expeted the output to be like this:
<div class="container container-collapsed">
<div class="actions-panel_true"></div>
<div class="units-wrapper">
<div class="unit1"></div>
<div class="unit2"></div>
<div class="unit3"></div>
</div>
</div>
And instead I'm getting:
<div class="container container-collapsed">
<div class="actions-panel_true"></div>
<div class="units-wrapper"><div>
</div>
I know that my foreach is working, I debbuged it. But it is not building one JSX per units as I expcted. How to I make this foreach loop return one component for each object in my array, back to the JSX i'm trying to render?

You need .map instead of .forEach to return an array so that React can render it effectively.
Also returning from the callback inside .forEach has no use since .forEach doesn't return an array. It returns undefined.
.forEach is useful when you want to change the contents of existing array without returning a new one. In React, JSX requires an array as a result of .map operation.
Also more valid points covered by #zhulien

This should do:
ReactDOM.render((
<div className="container container-collapsed">
<Actions Units={aUnits} />
<div className="units-wrapper">
{aUnits.map(unit => {
return (
<Unit key={u.key} unit={u} />
);
})}
</div>
</div>
), document.getElementById("root"));
A couple notes:
<Unit={u} />
is not a valid syntax. You are suppose to assign u to a property of the Unit component, not the component itself. Also, when rendering array of items, you should supply a key property to each item as to help React differentiate between them. You'll find more information about why this is important In the React docs.

Related

firebase Inline If-Else with Conditional Operator insinde of Inline If-Else with Conditional Operator

Hello I'am trying to render the JSX from my component dynamically, some object from my database have other variables then others. I only want to render the HTML elements when the needed Data is provided. But this i causing me this problem:
Syntax error: Adjacent JSX elements must be wrapped in an enclosing
tag (202:38)
. I know it for sure the JSX elements are all closed. I think it is because it is an If statement inside an if statement. thank you in advance for your help.
<div>
{this.state.loading ?(
<div className="wrapper">
<Navbar/>
<Body>
{this.state.snap.someValue ?
(<div className="detailsCategory col-sm-5 col-md-5">
Heading
</div>
<div className="col-sm-5 col-md-5">
<p>{this.state.snap.childValue}</p>
</div>):(null)}
</Body>
</div>)
:
(<h1> LOADING... </h1>)}
</div>

Learning React.js

I am trying to learn React. I already have a good grasp of javascript. I am trying to learn by creating a small app that is basically a task manager. In my case it's for grocery related items. I have a fiddle created here. Could you please take a look at how I composed the react code and let me know if this is the best approach to building with components/classes? You can see that I have nested components. I am not sure if there is a better way of doing this.
Finally, I wan't a new "add-item-row" created every time a user clicks on the big blue Add button. Right now one is showing be default but I don't want any showing by default. I want one created (add-item-row, div) only when a user clicks on the Add button.
Here is the fiddle.
https://jsfiddle.net/j0mpsbh9/4/
<div id="app" class="container">
<script type="text/babel">
var AddItemWrapper = React.createClass({
render: function() {
return (
<div>
<div className="row">
<AppTitle />
<AddItemForm />
</div>
<AddItemRow />
</div>
);
}
});
var AppTitle = React.createClass({
render: function() {
return (
<div>
<h1>Grocery List</h1>
</div>
);
}
});
var AddItemForm =React.createClass({
render: function() {
return (
<div>
<div className="col-sm-6 col-lg-6">
<div className="form-group">
<label htmlFor="enter-grocery-item" className="sr-only">Enter Grocery Item</label>
<input type="text" className="form-control" id="enter-grocery-item" placeholder="Enter Grocery Item" />
</div>
</div>
<div className="col-sm-6 col-lg-6">
<button type="button" id="add" className="btn btn-block btn-info">Add <span className="glyphicons circle_plus"></span></button>
</div>
</div>
);
}
});
var AddItemRow =React.createClass({
render: function() {
return (
<div className="add-item-row">
<div className="row">
<div className="col-sm-12 grocery-items">
<div className="col-sm-6">
<div className="form-group">
<label htmlFor="grocery-item" className="sr-only">Grocery Item</label>
<input type="text" className="form-control" id="grocery-item" placeholder="" />
</div>
</div>
<div className="col-sm-6 center">
<button type="button" className="btn btn-blockx btn-warning"><span className="glyphicons pencil"></span></button>
<button type="button" className="btn btn-blockx btn-lgx btn-danger"><span className="glyphicons remove"></span></button>
<button type="button" className="btn btn-blockx btn-lgx btn-success"><span className="glyphicons thumbs_up"></span></button>
</div>
</div>
</div>
</div>
);
}
});
ReactDOM.render(
<AddItemWrapper />,
document.getElementById('app')
);
</script>
</div>
You have a good start here! Your component hierarchy is organized in a sensible way. However you are missing any kind of interactivity or internal state.
The main way you make React components interactive is by using state plus event callbacks which modify said state. "State" is pretty self explanatory - it describes values inherent to how the components looks and behaves, but which change over time. Every time a React component's state is altered (with this.setState()) that component will re-render (literally by re-running the render() function) to reflect the changes.
First let's edit your AddItemWrapper class to keep track of some internal state when it is first mounted. We know that you want to have multiple rows of data, so let's give it an empty array to store future information about rows:
getInitialState(){
return {rows: []};
},
Now instead of rendering a single AddItemRow directly, we'll render a dynamic set of rows that is based on the current component state. Array.map() is perfect for this and a common use case in React:
{this.state.rows.map(function(ea, i){
return <AddItemRow initialItemName={ea} key={ea + "-" + i} />
})}
Basically what that does is take every entry in the array AddItemWrapper.state.rows array and renders it as an AddItemRow component. We give it two properties, initialItemName and key. "initialItemName" will just tell the child component what its name was when it was first added, and "key" is a unique string that allows React to differentiate components from their siblings.
Now we've set up AddItemWrapper to properly render rows based on its internal state. Next we have to modify AddItemForm so that it will react to user input and trigger new rows being added.
In AddItemForm, first we need to add a "ref" to the input text box. This is so that React can identify and read data from this HTML element after it is rendered:
<input ref={function(el){this.inputElement = el;}.bind(this)} ... />
Then give the button element a callback that will trigger when it's clicked:
<button onClick={this.handleClick} ... />
Finally write the callback handler itself:
handleClick(){
this.props.onAdd(this.inputElement.value);
this.inputElement.value = "";
}
Notice how this callback is calling this.props.onAdd()? That means we need to pass in a callback function from the parent (AddItemWrapper) to this component to use. This is how we communicate between parents and children in React: pass a function from a parent to a child which will be triggered from within the child, but will effect the parent.
In AddItemWrapper we make sure AddItemForm has access to the callback function:
AddItemForm onAdd={this.onAdd} />
And then we write the callback function itself:
onAdd(newItem){
var newRows = this.state.rows.slice();
newRows.push(newItem);
this.setState({rows: newRows});
}
Notice how we're copying the old array held in state (using Array.slice()), push a new item into the new array, and then update state with the new array? Never mutate state directly; ALWAYS copy it, modify the copy, and then update state with the new copy.
Almost done. We've created a way for AddItemWrapper to render its rows, and a way for AddItemForm to create new rows. Now we edit AddItemRow to render in a way that maintains its own internal state too.
First make sure it initializes its own state when it's mounted. We'll have it keep track of a string value, which initially is the same as what the user entered into the text box when they pressed "Add", but because it's kept in AddItemRow.state it can be modified later by the user:
getInitialState() {
return {itemName: this.props.initialItemName}
}
Now that the row name is kept in the component state, we can render it in the HTML like this:
<input value={this.state.itemName} ... />
Here's what it looks like when you put it all together!
There are obviously more features that you would want to add, such as letting the user edit, move, or delete a row entry. I'll leave those exercises up to you. I highly recommend you read through all of the official documentation as well as do a few tutorials to get your head in the game. It's obvious that you have a bit of experience under your belt given what you had so far, but getting the hang of how state, render(), and callbacks work takes some practice. Good luck!

How to efficiently pass the same "prop" to every stateless presentation component?

I have what looks like it will be a simple and common case of non-DRY code - I'm hoping there's a way to avoid this.
I have a page to render information about an object based on a route like this:
<Route path="project/:projectid" component={ProjectDetails}/>
So... the 'project id' comes in as a prop from React Router, and every stateless presentation component needs the resulting project object:
class ProjectDetails = ({project}) => {
render() {
return (
<div className="project-details-page container-fluid">
<HomeLeftBar/>
<div className="col-md-10">
<ProjectHeaderBar project={project}/>
</div>
<div className="project-centre-pane col-md-7">
<ProjectActions project={project}/>
<ProjectDescription project={project}/>
<ProjectVariations project={project}/>
</div>
<div className="project-right-bar col-md-3">
<ProjectContacts project={project}/>
<ProjectCosting project={project}/>
</div>
</div>
)
}
}
export default connect(
state => ({project: state.projects[state.router.params.projectid]})
)(ProjectDetails)
How should I clean this up, so I don't have to pass the project to every component?
I've noted a couple of similar questions this and this at least. But while their titles are similar, their details are not. They seem to ask more advanced questions about more specific situations.
For me, passing the data down from the top via props is the clearest way to get data into your leaf components, so I think what you've done here is fine.
The bit that looks not-clean is that you're passing the whole project object to every component but at a glance, it looks as though each of those components is responsible for rendering only a portion of that project object.
Passing only the branches of project that each component needs will make the relationship between the data and the view much clearer.
<ProjectContacts project={project.contracts}/>
<ProjectCosting project={project.costing}/>
and in turn I think this will make the whole component feel cleaner:
class ProjectDetails = ({project: { costings, description, variations, title, actions, contacts} }) => {
render() {
return (
<div className="project-details-page container-fluid">
<HomeLeftBar/>
<div className="col-md-10">
<ProjectHeaderBar title={title}/>
</div>
<div className="project-centre-pane col-md-7">
<ProjectActions actions={actions}/>
<ProjectDescription description={description}/>
<ProjectVariations variations={variations}/>
</div>
<div className="project-right-bar col-md-3">
<ProjectContacts contacts={contacts}/>
<ProjectCosting costings={costings}/>
</div>
</div>
)
}
}
I assume you are using react-router. You can do something like this:
<Router history={history}>
<Route path="project/:projectId">
<Route path="participants" component={Participants} />
<Route path="progress" component={Progress}/>
</Route>
</Router>
All children of <Route path="project/:projectId"> have this.props.params.projectId available. For example, url to the Participants component is "project/:projectId/participants", and therefore projectId is a param when rendering Participants.
Also, if the project details should be rendered the same way on all pages, you can add a component on the Route to "project/:projectId", which is responsible for doing rendering the project details. (That component would receive Participants or Progress as this.props.children, which you'd have to render in the new component.)

How do I use a custom DOM node to render to from within a react component

I have the following problem:
I want to write jsx code like
<div className="my-section">
<Window>
<div>Window content</div>
</Window>
</div>
<div className="window-container">
</div>
somewhere in my react content, but I want the window to render in a special DOM element with other windows, something like
<div class="my-section"></div>
<div class="window-container">
<div class="window">
<div>Window Content</div>
</div>
</div>
And to do this transparently I need to tell the component to render to a special DOM node from within the component. Is there a way to do this? If not, how should I accomplish the functionality I am looking for from within React?
You're looking for the special this.props.children list.
When you create a React component instance, you can include additional React components or JavaScript expressions between the opening and closing tags like this:
<Parent><Child /></Parent>
Parent can read its children by accessing the special this.props.children prop.
This will allow you to get the children defined inside your element, then insert them at an arbitrary point for render.
render() {
return (
<div className="window container">
<div className="window">{this.props.children}</div>
<div className="window"></div>
...
</div>
);
}

How to avoid extra wrapping <div> in React?

Today I have started learning ReactJS and after an hour faced with the problem..
I want to insert a component which has two rows inside a div on the page.A simplified example of what I am doing below.
I have an html:
<html>
..
<div id="component-placeholder"></div>
..
</html>
Render function like this:
...
render: function() {
return(
<div className="DeadSimpleComponent">
<div className="DeadSimpleComponent__time">10:23:12</div >
<div className="DeadSimpleComponent__date">MONDAY, 2 MARCH 2015</div>
</div>
)
}
....
And below I am calling render:
ReactDOM.render(<DeadSimpleComponent/>, document.getElementById('component-placeholder'));
Generated HTML looks like this:
<html>
..
<div id="component-placeholder">
<div class="DeadSimpleComponent">
<div class="DeadSimpleComponent__time">10:23:12</div>
<div class="DeadSimpleComponent__date">MONDAY, 2 MARCH 2015</div>
</div>
</div>
..
</html>
The problem that I am not a very happy that React forcing me to wrap all in a div "DeadSimpleComponent". What is the best and simple workaround for it, without explicit DOM manipulations?
UPDATE 7/28/2017: Maintainers of React added that possibility in React 16 Beta 1
Since React 16.2, you can do this:
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
This requirement was removed in React version (16.0), so now you are able to avoid that wrapper.
You can use React.Fragment to render a list of elements without creating a parent node, official example:
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
More here: Fragments
Update 2017-12-05:
React v16.2.0 now fully supports rendering of fragments with improved support for returning multiple children from a components render method without specifying keys in children:
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
If you are using a React version prior to v16.2.0, it is also possible to use <React.Fragment>...</React.Fragment> instead:
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
Original:
React v16.0 introduced returning an array of elements in render method without wrapping it in a div: https://reactjs.org/blog/2017/09/26/react-v16.0.html
render() {
// No need to wrap list items in an extra element!
return [
// Don't forget the keys :)
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>,
];
}
At the moment, a key is required for each element to avoid the key warning but this could be changed in future releases:
In the future, we’ll likely add a special fragment syntax to JSX that
doesn’t require keys.
You can use:
render(){
return (
<React.Fragment>
<div>Some data</div>
<div>Som other data</div>
</React.Fragment>
)
}
For further details refer to this documentation.
Use [], instead of ()'s to wrap the entire return.
render: function() {
return[
<div className="DeadSimpleComponent__time">10:23:12</div >
<div className="DeadSimpleComponent__date">MONDAY, 2 MARCH 2015</div>
]
}
I created a component to wrap child components without a DIV. It's called a shadow wrapper: https://www.npmjs.com/package/react-shadow-wrapper
This is still required, BUT React now make sure to create elements without creating an additional DOM element.
The extra wrapping needed (normally with a parent div) because Reacts createElement method require a type parameter which is either a tag name string (such as 'div' or 'span'), a React component type (a class or a function). But this was before they introduce React Fragment.
Refer this NEW api doc for createElement
React.createElement : Create and return a new React element of the given type. The type argument can be either a tag name string (such as 'div' or 'span'), a React component type (a class or a function), or a React fragment type.
here is the official example, Refer React.Fragment.
render() {
return (
<React.Fragment>
Some text.
<h2>A heading</h2>
</React.Fragment>
);
}
I know this question has been answered, you can of course use React.Fragment which doesn't create a node but let's you group stuff like a div.
Additionally if you want to have fun you can implement (and learn lots of things) a React mode that removes the extra div's and for this I really want to share a great video on how you can do it on the react code base itself.
https://www.youtube.com/watch?v=aS41Y_eyNrU
This is of course not something that you would do in practice but it's a good learning opportunity.
You won't be able to get rid of that div element. React.render() needs to return one valid DOM node.
Here is one way to render "transculent" components:
import React from 'react'
const Show = (props) => {
if (props.if || false) {
return (<React.Fragment>{props.children}</React.Fragment>)
}
return '';
};
----
<Show if={yomama.so.biq}>
<img src="https://yomama.so.biq">
<h3>Yoamama</h3>
<Show>
There is workaround too. The below block code generates fragment without the need of React.Fragment.
return [1,2,3].map(i=>{
if(i===1) return <div key={i}>First item</div>
if(i===2) return <div key={i}>Second item</div>
return <div key={i}>Third item</div>
})

Categories

Resources