How to use the condition map function in ReactJS? - javascript

I am trying to display bootstrap carousel via ajax call using react js. Ajax receives json data consisting image name, content title and some meta information of per slide what I want to inject in DOM. So, I use the map function to generate all slides. My problem is, for the first slide I want to add a class active. But I do not know how to use condition in map().
In React, I have written: (in SliderWidget class, I have written a comment actually where I should use active class conditionally)
var HomeCarousel = React.createClass({
getInitialState: function() {
return {
data: []
}
},
componentDidMount: function() {
$.get("/api/slider", function(result) {
this.setState({
data: result
});
}.bind(this));
},
render: function() {
return (
<div id={"myCarousel"} className={"carousel slide"} data-ride="carousel">
{this.state.data.map((slider, i) => <SliderWidget key = {i} data = {slider} />)}
</div>
);
}
});
class SliderWidget extends React.Component {
render() {
return (
<div className={"item active"}> // here I want to use active class for the first slide
<img className={"first-slide"} src="images/pexels.jpeg" alt="First slide" />
<div className={"container"}>
<div className={"carousel-caption"}>
<h3>Research Indicates Breakfast is the Most Important Meal</h3>
<p><a className={"btn btn-primary"} href="#" role="button">Find Out More</a></p>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<HomeCarousel />, document.getElementById('react-home-carousel')
);

The i in the map callback is the loop index, so pass a property accordingly:
this.state.data.map( (slider, i) =>
<SliderWidget key={i} data={slider} active={i===0} />
)
Then in SliderWidget:
render() {
return (
<div className={"item" + this.props.active ? ' active' : ''}>...
)
}
Using classnames will make your life even easier.

Related

Returning multiple HTML snippets per element of array

Edited --
Let's say I have an array of JSON objects:
"social":[
{
"name":"facebook",
"className":"fa fa-facebook"
},
{
"name":"linkedin",
"className":"fa fa-linkedin"
},
{
"name":"instagram",
"className":"fa fa-instagram"
},
{
"name":"github",
"className":"fa fa-github"
}
]
How do I create an snippet for each of the objects such that they return
<p>{social.name}<p>
And I don't want to use map.
This is generalized for a more complicated example, but this seems to be the problem I am facing (i.e. I have the data in the format below and I need to get the property from each of the elements to display and I only have one function)
Assuming that social is a part of the state, you can implement a method that maps each item in the social array to a p tag:
renderSocialNames = () => {
return this.state.social.map(
socialItem => <p key={socialItem.className}>{socialItem.name}</p>
);
}
Here's a Working Sample StackBlitz for your ref.
cleaner code :) , this might solve your issue
import React, { Component } from "react";
class Projects extends Component {
constructor(props) {
//initialize this component with props
super(props);
}
render() {
const { data } = this.props;
if (data) {
const projects = data.map(project => {
return (
<a className="cell" data-remodal-target={project.id}>
<img
className="grid-image"
src={project.cover}
data-aload={projects.cover}
alt={project.name}
/>
</a>
);
});
const modals = data.map(project => {
return (
<div className="remodal" data-remodal-id={project.id}>
<button
data-remodal-action="close"
className="remodal-close"
></button>
<h1>Remodal</h1>
<p>
Responsive, lightweight, fast, synchronized with CSS animations,
fully customizable modal window plugin with declarative
configuration and hash tracking.
</p>
<br />
<button data-remodal-action="cancel" className="remodal-cancel">
Cancel
</button>
<button data-remodal-action="confirm" className="remodal-confirm">
OK
</button>
</div>
);
});
}
return (
<section id="projects">
<div className="grid-container remodal-bg">
{projects}
{modals}
</div>
</section>
);
}
}

ReactJSL How to target a single element in a map function with a button

I have a Component that renders a list of elements using the map function. Each element is rendered with a delete and edit button. I have added the delete functionality, but I'm having problem with the edit one.
The functionality that I want is: click on edit item, replace H3 element (which is the title) with an input field and let the user update the name. I've tried replacing an element with another but this only works for the first element of the list, because I get the element with 'getElementById' I have tried doing it with querySelector, but that selects only the last element of the array.
I have no idea what to do. I know the issue is selecting the particular element at the right index. I use an id as a key but I don't know how to properly replace the html element. Any help will be vastly appreciated.
Here is where the map function renders the elements:
class Donut extends Component {
render(){
const {donuts, deleteDonut, editDonut} = this.props;
const donutsList = donuts.map((donut) => {
return <div key={donut.id} className="donut">
<div className="name">
<img src={donut.image} />
<div id="donut-name">
<h3 id="donut-title">{donut.name}</h3>
<p>{donut.date}</p>
</div>
</div>
<div className="price">
<p>{donut.price}</p>
<img src="img/edit.png" id={donut.id} onClick={()=>{editDonut(donut.id)}} />
<img src="img/delete.png" id={donut.id} onClick={() => {deleteDonut(donut.id)}} />
</div>
</div>
})
return (
<div>
{donutsList}
</div>
)
}
}
export default Donut
Try to avoid as much as possible directly manipulating DOM elements when you using React. In this case, you should use another approach:
Add a field to this class's state: editingDonutId
When you click in a donut, set the editingDonutId to corresponding id and when you finished it, reset the value.
In render function, inside the map, do a condition render to check if current rendering donut has same id with editingDonutId, if true, we render an input instead.
You are using react, not jquery, so do not use getElementById, try react solution.
This is my solution:
class Donut extends Component {
state = {
donutsState: {}
}
setDonutState: (id, value) => {
this.setState((preState) => {
const predonutState = preState.donutsState[id] || {}
return {
donutsState: {
...preState.donutsState,
[id]: {
...predonutState,
...value,
}
}
}
})
}
getDonutState: (id) => this.state.donutsState[id] || {};
render(){
const {donuts, deleteDonut, editDonut} = this.props;
const donutsList = donuts.map((donut) => {
const donutState = this.getDonutState(donut.id)
// when user input the name, save it in the state.
const onChange = (e) => {
this.setDonutState(donut.id, { value: e.target.value })
}
// when click edit, replace h3 with input.
const onEdit = () => {
this.setDonutState(donut.id, { eidt: true })
}
// when enter key, replace input with h3 and submit the name value.
const onKeyDown = (e) => {
if (e.key === 'Enter') {
this.setDonutState(donut.id, { eidt: false })
editDonut(donut.id, {
name: this.getDonutState(donut.id).value || donut.name,
})
}
}
return (
<div key={donut.id} className="donut">
<div className="name">
<img src={donut.image} />
<div id="donut-name">
{
donutState.edit
? <input id="edit-donut-title" value={donutState.value || donut.name} onChange={onChange} onKeyDown={onKeyDown} />
: <h3 id="donut-title">{donut.name}</h3>
}
<h3 id="donut-title">{donut.name}</h3>
<p>{donut.date}</p>
</div>
</div>
<div className="price">
<p>{donut.price}</p>
<img src="img/edit.png" id={donut.id} onClick={()=>{editDonut(donut.id)}} />
<img src="img/delete.png" id={donut.id} onClick={() => {deleteDonut(donut.id)}} />
</div>
</div>
)
})
return (
<div>
{donutsList}
</div>
)
}
}
export default Donut

Conditional rendering on React.js

render() {
const tableStyle = this.getTableStyle();
const tableSettings = this.getTableSettings();
return (
<div style={tables}>
<TablePosition
contextMenuOn={true}
step={this.props.step}
pdfData={this.props.pdfData}
tableSettings={tableSettings}
tableStyle={tableStyle}
fileName={this.state.fileName}
tableSize={this.getTableSize()}
tableOffset={this.state.tableOffset}
desiredWidth={700}
updateXOffset={x => this.updateXOffset(x)}
updateYOffset={y => this.updateYOffset(y)}
markTable={() => this.markTable()}
setOutputLabels={(row, col, val) => this.setOuputLabels(row, col, val)}
/>
</div>
);
if (!this.props.isThirdStep) {
return (
<div>
<div style={sideBySide}>
<PDFViewer
isThirdStep={this.props.isThirdStep}
paginationCallback={this.handlePageChange}
pdfData={this.state.pdfData}
desiredWidth={600}
selectedPage={this.props.savedPageNo}
/>
</div>
</div>
);
} else {
return (
<div>
<ReferenceMenu />
</div>
);
}
}
In my component's render, I try to render several components based on certain conditions.
So, basically, the TablePoisition always stays there, and the PDFViewer and ReferenceMenu renders conditionally.
However, what I see on both conditions is only the TablePosition component.
Is this not supposed to work?
As explained since you want to combine two components you should change your render logic. One component will be sit there always and the other one will be rendered conditionally. So, you need to render that last component with the sticky one in the same return. I would do something like this:
renderPDFViewer = () => (
<div>
<div style={sideBySide}>
<PDFViewer
isThirdStep={this.props.isThirdStep}
paginationCallback={this.handlePageChange}
pdfData={this.state.pdfData}
desiredWidth={600}
selectedPage={this.props.savedPageNo}
/>
</div>
</div>
);
render() {
const tableStyle = this.getTableStyle();
const tableSettings = this.getTableSettings();
return (
<div>
<div style={tables}>
<TablePosition
contextMenuOn={true}
step={this.props.step}
pdfData={this.props.pdfData}
tableSettings={tableSettings}
tableStyle={tableStyle}
fileName={this.state.fileName}
tableSize={this.getTableSize()}
tableOffset={this.state.tableOffset}
desiredWidth={700}
updateXOffset={x => this.updateXOffset(x)}
updateYOffset={y => this.updateYOffset(y)}
markTable={() => this.markTable()}
setOutputLabels={(row, col, val) => this.setOuputLabels(row, col, val)}
/>
</div>
{
!this.props.isThirdStep
? this.renderPDFViewer()
: ( <div><ReferenceMenu /></div> )
}
</div>
);
}
You need to place your conditional renders inside variables or something similar.
var conditionContent1 = null;
var conditionContent2 = null;
if(condition1){
conditionContent1 = <div>conditional content 1</div>;
}
if(condition2){
conditionContent2 = <div>conditional content 2</div>;
}
return (
<div id="wrapper">
<div>
content
</div>
{conditionContent1}
{conditionContent2}
</div>
);
I added a wrapper div; because, I believe render's return doesn't like having multiple root elements.
If the variables are null; then, it won't affect the overall render.

Rendering an array.map() in React

I am having a problem where I am trying to use array of data to render a <ul> element. In the code below the console logs are working fine, but the list items aren't appearing.
var Main = React.createClass({
getInitialState: function(){
return {
data: dataRecent
}
},
render: function(){
return (
<div>
<ul>
{
this.state.data.map(function(item, i){
console.log('test');
<li>Test</li>
})
}
</ul>
</div>
)
}
});
ReactDOM.render(<Main />, document.getElementById('app'));
What am I doing wrong? Please feel free to point out anything that isn't best practice.
Gosha Arinich is right, you should return your <li> element.
But, nevertheless, you should get nasty red warning in the browser console in this case
Each child in an array or iterator should have a unique "key" prop.
so, you need to add "key" to your list:
this.state.data.map(function(item, i){
console.log('test');
return <li key={i}>Test</li>
})
or drop the console.log() and do a beautiful oneliner, using es6 arrow functions:
this.state.data.map((item,i) => <li key={i}>Test</li>)
IMPORTANT UPDATE:
The answer above is solving the current problem, but as Sergey mentioned in the comments: using the key depending on the map index is BAD if you want to do some filtering and sorting. In that case use the item.id if id already there, or just generate unique ids for it.
You are not returning. Change to
this.state.data.map(function(item, i){
console.log('test');
return <li>Test</li>;
})
let durationBody = duration.map((item, i) => {
return (
<option key={i} value={item}>
{item}
</option>
);
});
Using Stateless Functional Component We will not be using this.state. Like this
{data1.map((item,key)=>
{ return
<tr key={key}>
<td>{item.heading}</td>
<td>{item.date}</td>
<td>{item.status}</td>
</tr>
})}
You are implicitly returning undefined. You need to return the element.
this.state.data.map(function(item, i){
console.log('test');
return <li>Test</li>
})
Best Answer:
import React, { useState } from 'react';
import './App.css';
function App() {
// Array of objects containing our fruit data
let fruits = [
{ label: "Apple", value: "🍎" },
{ label: "Banana", value: "🍌" },
{ label: "Orange", value: "🍊" }
]
// Using state to keep track of what the selected fruit is
let [fruit, setFruit] = useState("⬇️ Select a fruit ⬇️")
// Using this function to update the state of fruit
// whenever a new option is selected from the dropdown
let handleFruitChange = (e) => {
setFruit(e.target.value)
}
return (
<div className="App">
{/* Displaying the value of fruit */}
{fruit}
<br />
<select onChange={handleFruitChange}>
{
fruits.map((fruit) => <option value={fruit.value}>{fruit.label}</option>)
}
</select>
</div>
);
}
export default App;
Add up to Dmitry's answer, if you don't want to handle unique key IDs manually, you can use React.Children.toArray as proposed in the React documentation
React.Children.toArray
Returns the children opaque data structure as a flat array with keys assigned to each child. Useful if you want to manipulate collections of children in your render methods, especially if you want to reorder or slice this.props.children before passing it down.
Note:
React.Children.toArray() changes keys to preserve the semantics of nested arrays when flattening lists of children. That is, toArray prefixes each key in the returned array so that each element’s key is scoped to the input array containing it.
<div>
<ul>
{
React.Children.toArray(
this.state.data.map((item, i) => <li>Test</li>)
)
}
</ul>
</div>
I've come cross an issue with the implementation of this solution.
If you have a custom component you want to iterate through and you want to share the state it will not be available as the .map() scope does not recognize the general state() scope.
I've come to this solution:
`
class RootComponent extends Component() {
constructor(props) {
....
this.customFunction.bind(this);
this.state = {thisWorks: false}
this.that = this;
}
componentDidMount() {
....
}
render() {
let array = this.thatstate.map(() => {
<CustomComponent that={this.that} customFunction={this.customFunction}/>
});
}
customFunction() {
this.that.setState({thisWorks: true})
}
}
class CustomComponent extend Component {
render() {
return <Button onClick={() => {this.props.customFunction()}}
}
}
In constructor bind without this.that
Every use of any function/method inside the root component should be used with this.that
Dmitry Brin's answer worked for me, with one caveat. In my case, I needed to run a function between the list tags, which requires nested JSX braces. Example JSX below, which worked for me:
{this.props.data().map(function (object, i) { return <li>{JSON.stringify(object)}</li> })}
If you don't use nested JSX braces, for example:
{this.props.data().map(function (object, i) { return <li>JSON.stringify(object)</li>})}
then you get a list of "JSON.stringify(object)" in your HTML output, which probably isn't what you want.
import React, { Component } from 'react';
class Result extends Component {
render() {
if(this.props.resultsfood.status=='found'){
var foodlist = this.props.resultsfood.items.map(name=>{
return (
<div className="row" key={name.id} >
<div className="list-group">
<a href="#" className="list-group-item list-group-item-action disabled">
<span className="badge badge-info"><h6> {name.item}</h6></span>
<span className="badge badge-danger"><h6> Rs.{name.price}/=</h6></span>
</a>
<a href="#" className="list-group-item list-group-item-action disabled">
<div className="alert alert-dismissible alert-secondary">
<strong>{name.description}</strong>
</div>
</a>
<div className="form-group">
<label className="col-form-label col-form-label-sm" htmlFor="inputSmall">Quantitiy</label>
<input className="form-control form-control-sm" placeholder="unit/kg" type="text" ref="qty"/>
<div> <button type="button" className="btn btn-success"
onClick={()=>{this.props.savelist(name.item,name.price);
this.props.pricelist(name.price);
this.props.quntylist(this.refs.qty.value);
}
}>ADD Cart</button>
</div>
<br/>
</div>
</div>
</div>
)
})
}
return (
<ul>
{foodlist}
</ul>
)
}
}
export default Result;

I could not dispaly noroom component if there is no place name that user has queried

I have a search form where user search for place. If the place that user has typed is in the api (response object) then i want to display Room component else NoRoom component. When i type the place that is not in the api , my NoRoom component is not displayed.
search( query='place' ){
let url = "http://localhost:8000/api/v1/rental/?place__startswith="+encodeURIComponent(query);
Request.get(url).then((response) => {
console.log('response',response.body.objects);
this.setState({
place:response.body.objects,
});
});
}
searchUpdated(term){
this.search(term);
}
render() {
var margin = { marginTop : '13em' };
if (this.state.place){
let location = _.map(this.state.place, (place,id) => {
return(
<Room key={id}
slug={place.slug}
place={place.place}
city={place.city}
gallery={place.gallery}
property={place.property}/>
)
console.log('location',location);
});
let gallery = _.map(this.state.place, (place,id) => {
console.log('place',place.gallery);
_.map(place.gallery, (image,id) => {
return(
<img src={image.image} class="img-fluid" />
)
});
});
return(
<div className = "container">
<div className="content text-align-center">
<div className="row text-xs-center">
<div className="middle-text" style={margin}>
<h1 className="welcome"><span>Rental Space Welcome's you </span></h1>
<button ref="test" className="btn how-it-works" onClick={this.handleClick}>Search Space</button>
</div>
</div>
</div>
<div id="mySearch" className="overlay" onKeyDown={this.handleKeyDown}>
<button className="btn closebtn" onClick={this.handleClick}>x</button>
<div className="overlay-content">
<SearchInput ref="searchInput" className="search-input" onChange={this.searchUpdated} />
<div className="container searchList">
{ location }
</div>
</div>
</div>
</div>
);
}
else{
return(
<NoRoom />
)
}
}
}
class Room extends React.Component{
render(){
let imageFile = this.props.gallery.map((image) => {
return(
<img src={image.image} className="img-fluid" width="250px" height="250px" />
);
});
return(
<div className="room">
<div className="thumbnail">
{ imageFile[0] }
</div>
<h3 className="listingName text-left">
<a href = { "/rent/" + this.props.slug }>{this.props.place}</a>
</h3>
<span className="propertySpan">
<i className = "fa fa-home"></i>
<span className="property">{this.props.property}</span>
</span>
</div>
)
}
}
class NoRoom extends React.Component{
render(){
return(
<div>No Room</div>
)
}
}
You can see in image, No Room text is not displayed when there is no place called kat
What have i done wrong?
In you root-component, your local state (this.state.place) is an array that defines whether you display the Room component or the NoRoom component.
If your API doesn't find any matching place, then this.state.place is an empty array, which is truthy. That's why you just have to check the length of the array in your render method:
if (this.state.place.length > 0) {
Detailed explanation:
Your UI behavior is defined by the component state. The initial structure of this state must be described in your component, this is what you do in the component constructor:
this.state = {place: []};
This initial state will be used during the 1st rendering of the component.
Then, each time you want to update this state, you call this.setState() with a new value for the property "place", which must be an array for consistency.
In your render() method, you just have to describe your UI according to the current value of your state. Because "place" is always an array, the only thing you can do to check if you have data in it is to test the "length" property of this array (length === 0 when no data). If you only check the array itself like you did initially (if (this.state.place) { ... }), it will always evaluate to "true" (because an array, even empty, is always "truthy") and it's not what you want.

Categories

Resources