why data is not rendering using this.props in react js? - javascript

I am breaking down my Header into components. Ex: HaderTop, HeaderBottom. Further I m breaking HeaderBottom into more components. One of the component of HeaderBottom is "HeaderTab". I am passing JSON OBJECT DATA through "HeaderTab" component, which I want to loop using "this.props.data.map()" in HeaderTab class. I am not getting any error, but I am not getting the view also. Below is piece of code. "Below I have attached the view also (My UI should look like this)."
import React from 'react';
import { Link } from 'react-router';
import HeaderBreadcrumb from './HeaderBreadcrumb';
import HeaderTab from './HeaderTab';
export default class HeaderBottom extends React.Component {
constructor(props) {
super(props);
this.state = {
tabs:
[
{"id":1,"name":"Dental","path":"dentalplan"},
{"id":2,"name":"Views","path":"dentalplan"},
{"id":3,"name":"Accident Companion","path":"dentalplan"},
{"id":4,"name":"Critical Illness","path":"dentalplan"},
{"id":5,"name":"Hospital Confinement","path":"dentalplan"}
]
}
}
render() {
return (
<div id="layout-navigation" className="layout-navigation">
<div className="content-heading">
<div className="container">
<HeaderBreadcrumb />
<div className="content-heading__title">
<h1>Dental Plans</h1>
</div>
<HeaderTab data={this.state.tabs}/>
</div>
</div>
</div>
);
}
}
import React from 'react';
import { Link } from 'react-router';
export default class HeaderTab extends React.Component {
render() {
return (
<div className="content-heading__tabs" data={this.props.data}>
<ul className="tabs tabs--horizontal tabs--overlay">
{
this.props.data.map(function(item, i) {
<li role="presentation" className="tab">
<Link to={item.path}>{item.name}</Link>
</li>
})
}
</ul>
</div>
);
}
}

I think you need to return an object in the map function :
this.props.data.map(function(item, i) {
return (
<li role="presentation" className="tab">
<Link to={item.path}>{item.name}</Link>
</li>
)
})

you are not returning a value in map function.
Try:
{
this.props.data.map(function(item, i) {
return <li role="presentation" className="tab">
<Link to={item.path}>{item.name}</Link>
</li>;
})
}
I see you are using es6 classes. You should also try https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions.

Related

Getting a Render not Function Error, when I run this Code Context API and Reactjs

Thanks for taking the time to read this. I have been at this now for a few hours and nearly got it right except now, Im Getting a "TypeError: render is not a function" Error and Can not figure it out, using Context API and Reactjs Please help
I have changed around States and props and back again this is the closest I got it to work
this is my parent file where I pass the UserProvider
import React, { Component } from "react";
import Group from "./Groups";
import JsonData from "../data/export";
import { UserProvider } from "./context/UserContext";
class GroupGrid extends Component {
render() {
// console.log(JsonData);
return (
<UserProvider value={JsonData}>
<div className='cards'>
<Group />
</div>
</UserProvider>
);
}
}
export default GroupGrid;
this is the child file
import React, { Component } from "react";
import UserContext from "../components/context/UserContext";
import { UserConsumer } from "../components/context/UserContext";
class GroupDetails extends Component {
static contextType = UserContext;
componentDidMount() {
const data = this.context;
// console.log(data); // { name: 'Tania', loggedIn: true }
}
render() {
return (
<UserConsumer>
{this.context.TransportGrid.GROUPS.map((value, index) => (
<div key={value.GROUP_ID} className='GroupDetail'>
<div className='groupRow1'>
<span className='Date_label'>Date:</span>
<span className='GroupDate'>
{value.GROUP_DATE}
</span>
<span className='Group_label'>Group No:</span>
<span className='GroupID'>{value.GROUP_ID}</span>
</div>
<div className='groupRow2'>
<span className='Driver_label'>Driver:</span>
<span className='DriverName'>
{value.DRIVER_NAME}
</span>
<span className='Reg_label'>Artic:</span>
<span className='VehcileReg'>
{value.VEHICLE_REG}
</span>
</div>
</div>
))}
</UserConsumer>
);
}
}
export default GroupDetails;
and this an example of the JSON it looking at
{
"TransportGrid": {
"GROUPS": [
{
"GROUP_ID": "1234",
"GROUP_DATE": "20/08/2019",
"DRIVER_ID": "22",
"DRIVER_NAME": "JIMMY BLOGGS",
"VEHICLE_REG": "XSRFDFDDF",
"START_COUNTRY": "COUNTRY1",
"END_COUNTRY": "COUNRTY2",
"MOVEMENTS": [
{
this is repeated like 180 times ANyone got any idea's?
Thanks in Advance
Problem is that your consumer from context API needs to just have a single children element as a function whereas you are having an array of JSX elements as children.
Also since you are using the latest context API you don't need to use the Consumer component, you could simply write your code as
class GroupDetails extends Component {
static contextType = UserContext;
componentDidMount() {
const data = this.context;
// console.log(data); // { name: 'Tania', loggedIn: true }
}
render() {
return (
<React.Fragment>
{this.context.TransportGrid.GROUPS.map((value, index) => (
<div key={value.GROUP_ID} className='GroupDetail'>
<div className='groupRow1'>
<span className='Date_label'>Date:</span>
<span className='GroupDate'>
{value.GROUP_DATE}
</span>
<span className='Group_label'>Group No:</span>
<span className='GroupID'>{value.GROUP_ID}</span>
</div>
<div className='groupRow2'>
<span className='Driver_label'>Driver:</span>
<span className='DriverName'>
{value.DRIVER_NAME}
</span>
<span className='Reg_label'>Artic:</span>
<span className='VehcileReg'>
{value.VEHICLE_REG}
</span>
</div>
</div>
))}
</React.Fragment>
);
}
}
export default GroupDetails;

How to pass data between two components in angularjs syntax es6?

I would like to pass data from the todos component to the todo component and display all elements in the form of a list.
I download data from the service and place it in the todos.controller. In todos.component, I use bindings: {todos: '<'}. In the todos.html view, it provides data in the form of todos = $ ctrl.todos. In todo.html, it iterates over todos and wants to return todo.name. Effect: returns only 'Todo'.
todo.service.js
export class TodoService {
constructor($http) {
'ngInject';
this.$http = $http;
}
getTodos() {
return this.$http.get('/api/todos');
}
}
todos.controller.js
class TodosController {
constructor(TodoService) {
'ngInject'
this.TodoService = TodoService;
}
$onInit() {
this.todos = null;
this.TodoService.getTodos().then(response =>
{
this.todos = response.data;
console.log(this.todos);
});
}
}
export default TodosController;
todo.component.js
import template from './todo.html';
import controller from './todo.controller';
import './todo.scss';
let todoComponent = {
bindings: {
todos: '<'
},
template,
controller
};`
export default todoComponent;
todos.html
<h1>Todos</h1>
<ul>
<todo todos="$ctrl.todos"></todo>
</ul>
todo.html
<div>
<p ng-repeat='todo in $ctrl.todos track by $index'>Todo:
{{$ctrl.todo.name}}
</p>
</div>
Change todo.html as follows:
<div>
<p ng-repeat='todo in $ctrl.todos'>Todo:
̶{̶{̶$̶c̶t̶r̶l̶.̶t̶o̶d̶o̶.̶n̶a̶m̶e̶}̶}̶
{{todo.name}}
</p>
</div>
Also with todos.html:
<h1>Todos</h1>
̶<̶u̶l̶>̶
<todo todos="$ctrl.todos"></todo>
̶<̶/̶u̶l̶>̶
The only permitted content of <ul> elements is zero or more <li> elements.
For more information, see
MDN HTML Reference - <ul> element

Fallback message for empty object meteor/react

I would like to show a message if there aren't objects to be returned. Something like: 'currently there are no customers available'.
I tried to tinker a bit with Object.getOwnPropertyNames() but could not get it working because the mapping function does not being called. I'm not sure where to put this check, inside the rendering function, in the with tracker or render call inside the template.
I use Meteor/react and my code looks like this:
import React, {Component} from 'react';
import {withTracker} from 'meteor/react-meteor-data';
import {Link} from 'react-router-dom';
class ArchiveCustomerOverview extends Component {
renderCustomerList() {
return this.props.users.map( user => {
return(
<div className="row" key={user._id}>
<Link to={}>
<span className="medium-1">{user.profile.name}</span>
<span className="medium-4">{user.profile.company}</span>
<span className="medium-3">{user.profile.phone}</span>
<span className="medium-3">{user.emails[0].address}</span>
</Link>
</div>
)
});
}
render() {
return (
<div>
<div className="list-overview">
<div className="list-wrapper">
<div className="list-box clear">
{this.renderCustomerList()}
</div>
</div>
</div>
</div>
);
}
}
export default withTracker( (props) => {
Meteor.subscribe('users');
return {
users: Meteor.users.find({
'profile.isArchived': 0,
'roles.customers' : 'customer'
}).fetch()
};
})(ArchiveCustomerOverview);
Just check on the number of users before you render them like this:
renderCustomerList() {
if (this.props.users.length === 0) {
return (<div>Currently there are no customers available</div>)
}
return this.props.users.map( user => {
But a word of warning: you may not get what you want from the users collection - for security reasons it is treated differently from other collections.

Proper way to build bootstrap modals and notifications in React JS?

I would like to have modals and notifications in my app and coming from using old jQuery Bootstrap, creating modals and notifications were really easy but now I am pretty confused on how to implement this in the virtual DOM using the react component system.
This is what I believe the standard react way to build modals in React within a component:
Index/Router Component >
Main Layout Component >
{...Page Components... }
{...Child Component}
{<Modal /> or <Notification />}
The issue with this is I dont want to constantly have to import and create a <Modal> or <Notification /> component within my sub components, instead maybe just call a utility function such as {app.notify({type: 'success', message: 'some message'})} or app.modal({...customconfig}) and have both defined within my Main layout component which get triggerd through any child components.
Any help on this would be great, thanks!
You do not need to keep your Modal component in a hierarchy. Your Modal component should be an independent component which would take appropriate props to decide what needs to be displayed. E.g.
<Modal message={"This is my modal"} showOkCancel={true} showYesNo={false} handleOkYes={()=>console.log("OK clicked")} handleCancelNo={()=>console.log("Cancel clicked"} />
In the above example, the Modal accepts a number of props which would help it decide the message to display, the buttons to display and the actions that need to take on said button click.
This kind of a component can reside outside your component hierarchy and can be imported into any component that needs to show a modal. The parent component would just need to pass the appropriate props to show the modal.
Hope this helps.
So here is the approach I took to resolve this.
First here is how you want to structure the modal and notification components:
{Index/Router Component}
{Main Layout Component <Modal /> or <Notification />}
{...Page Components... }
{...Child Component calls app.modal({...config}) or app.notify(...config)}
For notifications, I used a plugin called react-notification-system and for modal, I just wrote it myself.
Here is my code:
Layout.js
import React from "react";
import {Link} from 'react-router';
import NotificationSystem from 'react-notification-system';
import AppHeader from "#/ui/header/AppHeader";
import AppFooter from "#/ui/footer/AppFooter";
import Modal from "#/ui/modals/modal/Modal";
import "#/main.scss";
import './layout.scss';
export default class Layout extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
app.notify.clear = this.refs.notificationSystem.clearNotifications;
app.notify = this.refs.notificationSystem.addNotification;
app.modal = this.refs.modal.updateProps;
}
render() {
return (
<div class="app">
<div class="header">
<AppHeader page={this.props.location.pathname.replace('/', '')}/>
</div>
<div class="body">
{this.props.children}
</div>
<div class="footer">
<AppFooter />
</div>
<NotificationSystem ref="notificationSystem" style={false} />
<Modal ref="modal" />
</div>
);
};
}
Modal.js
import React from "react";
import ReactDOM from 'react-dom';
import SVGInline from "react-svg-inline";
import {closeSvg} from '#/utils/Svg';
export default class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
showHeader: true,
showFooter: false,
title: "",
size: '',
className: '',
id: '',
footerContent: null,
showSubmitBtn: true,
showCancelBtn: true,
cancelBtnText: "Cancel",
successBtnText: "Save Changes",
onModalClose: () => {},
showModal: false,
html: () => {}
}
this.updateProps = this.updateProps.bind(this);
this.hideModal = this.hideModal.bind(this);
}
componentWillMount() {
var self = this;
var $modal = $(ReactDOM.findDOMNode(this));
}
componentDidUpdate(prevProps, prevState) {
if(this.state.showModal) {
$('body').addClass('modal-open');
} else {
$('body').removeClass('modal-open');
}
}
componentWillUnmount() {
// $('body').removeClass("modal-open");
}
componentWillReceiveProps(nextProps) {
console.log(nextProps);
}
updateProps(args) {
let merged = {...this.state, ...args};
this.setState(merged);
}
hideModal() {
this.setState({
showModal: false
});
this.state.onModalClose();
}
buildFooter() {
if(this.props.footerContent) {
return (
<div class="content">
{this.props.footerContent}
</div>
)
} else if(this.props.showCancelBtn && this.props.showSubmitBtn) {
return (
<div class="buttons">
<button type="button" class="btn btn-default" data-dismiss="modal" onClick={this.props.onModalClose}>{this.props.cancelBtnText}</button>
<button type="button" class="btn btn-success">{this.props.successBtnText}</button>
</div>
);
} else if(this.props.showCancelBtn) {
return (<button type="button" class="btn btn-default" data-dismiss="modal" onClick={this.props.onModalClose}>Close</button>);
} else if(this.props.showSubmitBtn) {
return (<button type="button" class="btn btn-success">Save changes</button>);
}
}
render() {
let {
id,
className,
onModalClose,
size,
showHeader,
title,
children,
showFooter,
showModal,
html
} = this.state;
return (
<div class={`modal-wrapper`} >
{
showModal ?
<div class={`modal fade in ${className}`} role="dialog">
<div class="bg" ></div>
<div class={`modal-dialog ${size}`}>
<div class="modal-content">
{ showHeader ?
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<SVGInline svg={closeSvg} />
</button>
<h4 class="modal-title">{ title }</h4>
</div> : '' }
<div class="modal-body" >
{html()}
</div>
{ showFooter ?
<div class="modal-footer">
{ this.buildFooter() }
</div> : ''
}
</div>
</div>
</div>
: ''
}
</div>
);
}
}
then in any of your child components you can just call within your render function:
app.notify({
message: message,
level: 'error'
});
or
app.modal({
showModal: true,
className: "fullscreen-image-modal",
size: "modal-lg",
html: () => {
return (<img src={listingManager.LISTINGS_PATH + imgUrl} />);
}
})

Is there any way I can manipulate a component's state, from a different component?

I have an ecosystem of 4 inter-related components and it'd be very helpful if I can manipulate one component's state from a different one.
This is my MidSection component:
import React, {Component} from 'react';
import {render} from 'react-dom';
import $ from 'jquery';
import List from './List';
import OpenSessionCard from './OpenSessionCard';
class MidSection extends Component {
constructor() {
super(...arguments);
this.state = {
cardsToBeDisplayed: this.props.sessionCards,
cardsFilter: 'All',
cardExpanded: false,
cardToBeDisplayed: null
};
}
filterCards() {
let selectedValue = $('#cards-filter').val();
if (selectedValue === 'All') {
this.setState({
cardsToBeDisplayed: this.props.sessionCards,
cardsFilter: 'All',
cardExpanded: false,
cardToBeDisplayed: null
});
} else {
this.setState({
cardsToBeDisplayed: this.props.sessionCards.filter((sessionCard) => sessionCard.status === selectedValue),
cardsFilter: selectedValue,
cardExpanded: false,
cardToBeDisplayed: null
});
}
}
render() {
let cardList, openSessionCard;
if (!this.state.cardToBeDisplayed) {
cardList = (
<List cards={this.state.cardsToBeDisplayed} filter={this.state.cardsFilter}/>
);
} else {
openSessionCard = (
<OpenSessionCard card={this.state.cardToBeDisplayed}/>
);
};
return (
<section className="col-md-8 col-sm-12 col-xs-12 middle-section-container">
<div className="nav-justified pull-left">
<div className="gray-background-color col-xs-12 form-control-static">
<div className="col-xs-6">
<label className="control-label green-color" htmlFor="inputError1">MY SESSIONS</label>
</div>
<div className="col-xs-2 col-xs-offset-4 text-right">
<select id="cards-filter" className="form-control" onChange={this.filterCards.bind(this)}>
<option>All</option>
<option>Open</option>
<option>Scheduled</option>
<option>Completed</option>
<option>Closed</option>
</select>
</div>
</div>
{cardList}
{openSessionCard}
</div>
</section>
);
}
componentDidMount() {
$('#mid-section').attr('data-rendered', 'true');
}
}
export default MidSection;
And this is my SessionCard component:
import React, {Component} from 'react';
import {render} from 'react-dom';
import $ from 'jquery';
import MidSection from './MidSection';
class SessionCard extends Component {
openCard() {
/*************
MidSection.setState({
cardsToBeDisplayed: null,
cardsFilter: null,
cardExpanded: true,
cardToBeDisplayed: this
});
**************/
$('#cards-filter').attr('disabled', 'disabled');
}
render() {
return (
<div className="card" onClick={this.openCard.bind(this)}>
<div className="card__title green-color">{this.props.name}</div>
<div className="card__details">
<span>Facilitator: {this.props.facilitator}</span><br/>
<span>Mode: {this.props.mode}</span><br/>
<span>Status: {this.props.status}</span><br/>
</div>
</div>
);
}
}
export default SessionCard;
I want the openCard() function in the SessionCard component to call the setState() function of the MidSection component. Is there any way I can achieve this? How do I refer to the MidSection component (with its current state) from the SessionCard component?
You should only set state from the main (parent) component. All children components should be "dumb" components. Pass in the function you are wanting to call as a prop of SessionCard like :
<OpenSessionCard card={this.state.cardToBeDisplayed} setStateFunc={this.setStateFunc}/>
And then in your openCard() function call :
this.props.setStateFunc();
This will call the function in the parent component and allow you to manipulate the state from there.
Thanks a lot #erichardson30! Even though your solution didn't totally suffice, yet it guided me to the right track. Passing the setState() function as a prop only passed the function, without the context (read: the component on which it is supposed to be called). So, I passed a reference to the MidSection component itself as a prop, and then in the openCard() function, I invoked the setState() function of the component in its props.
In my MidSection component:
<OpenSessionCard card={this.state.cardToBeDisplayed} midSectionComponent={this}/>
And in my openCard() function:
let midSectionComponent = this.props.midSectionComponent;
midSectionComponent.setState({
cardsToBeDisplayed: null,
cardsFilter: null,
cardExpanded: true,
cardToBeDisplayed: this
});
It's working exactly the way I wanted it to. Thanks again! :)

Categories