I have a react application wherein I want to render an array of menu items, and on click, show the expanded menu item's description. The array of menu items is in a separate file dishes.js
The list of menu items are rendered in a Menu component. On clicking, the expanded menu item is rendered through a DishDetail component.
dishes.js
export const DISHES =
[
{...}
]
MenuComponent.js
class Menu extends Component {
constructor (props) {
super (props);
this.state = {
selectedDish: null
}
}
onDishSelect(dish){
this.setState({ selectedDish: dish })
}
render() {
const menu = this.props.dishes.map((dish)=>{
return (
<div key = {dish.id} className="col-12 col-md-5 m-1"> {/* why do we need this key attribute whenever we construct a list of items in react, we need this key attribute When rendering elements on screen, the keys help react identify these elements uniquely */}
<Card onClick = {()=>this.onDishSelect(dish)}>
<CardImg width="100%" src= {dish.image} alt = {dish.name} />
<CardImgOverlay>
<CardTitle> {dish.name} </CardTitle>
</CardImgOverlay>
</Card>
</div>
);
});
return(
<div className="container">
<div className="row">
{menu}
</div>
<div className = "row">
<DishDetail p={this.state.selectedDish} />
</div>
</div>
);
}
}
export default Menu;
DishdetailComponent.js
class DishDetail extends Component {
constructor(props){
super(props);
this.state = {
}
}
render() {
if (p != null){
return(
<div className = "row">
<Card>
<CardImg width="100%" src={p.image} alt={p.name} />
<CardBody>
<CardTitle> {p.name} </CardTitle>
<CardText> {p.description}</CardText>
</CardBody>
</Card>
</div>
);
}
else {
return(
<div> </div>
);
}
}
}
export default DishDetail;
Output
./src/components/DishdetailComponent.js
Line 17:9: 'p' is not defined no-undef
Line 22:40: 'p' is not defined no-undef
Line 22:54: 'p' is not defined no-undef
Line 24:28: 'p' is not defined no-undef
Line 25:27: 'p' is not defined no-undef
Search for the keywords to learn more about each error.
Any help on this issue is greatly appreciated.
You're not defining p in the render function. Did you perhaps mean this.props.p?
class DishDetail extends Component {
constructor(props){
super(props);
this.state = {
}
}
render() {
const { p } = this.props;
if (p != null){
return(
<div className = "row">
<Card>
<CardImg width="100%" src={p.image} alt={p.name} />
<CardBody>
<CardTitle> {p.name} </CardTitle>
<CardText> {p.description}</CardText>
</CardBody>
</Card>
</div>
);
}
else {
return(
<div> </div>
);
}
}
}
export default DishDetail;
Related
I have a class in which I should add dynamically fields when the user click on "Add Button" or delete the fields if the user click on the "Remove Button".
export default class myClass extends Component
{
constructor(props){
super(props);
}
Fields = [
{
name: '$ClassName',
fields: [
{
name: 'ClassName.FirstField',
col: ["lg-6", 'md-6', 'sm-12'],
required: true,
label: "First Field"
}
]
},
]
render()
{
const self = this
return(
<Column col={'12'} className="mt-2 mb-2">
<Row>
{
this.Fields.map((group, i) => (
<Column key={`${group.name}_i`} col="12" className="mt-4 mb-2 form-fields-wrap">
<h5 className="form-section-title">{group.label}</h5>
<Row>
{
Start.renderFieldsGroup(group.fields, group.name, this.props.preview)
}
</Row>
</Column>
))
}
</Row>
</Column>
)
}
Now I should create the possibility to add (and remove) the Fields array when an user click on Add Button (or Remove Button).
How can I do to add dynamically add this field?
EDIT:
export default class myClass extends Component
{
constructor(props){
super(props);
this.state = { inputs: ['input-0'] };
}
tryFunction(){
const self = this
return(
<Column col={'12'} className="mt-2 mb-2">
<Row>
{
this.Fields.map((group, i) => (
<Column key={`${group.name}_i`} col="12" className="mt-4 mb-2 form-fields-wrap">
<h5 className="form-section-title">{group.label}</h5>
<Row>
{
Start.renderFieldsGroup(group.fields, group.name, this.props.preview)
}
</Row>
</Column>
))
}
</Row>
</Column>
)
}
appendInput() {
console.log("11111")
var newInput = `input-${this.state.inputs.length}`;
this.setState(prevState => ({ inputs: prevState.inputs.concat([newInput]) }));
}
render()
{
const self = this
return(
<div>
<div id="dynamicInput">
{console.log("this.state.input ", this.state.input)}
{this.state.inputs.map(input => this.tryFunction())}
</div>
<button onClick={ () => this.appendInput() }>
CLICK ME TO ADD AN INPUT
</button>
</div>
);
}
You call this.Fields.map() in your edit but as far as i can tell you dont actually declare Fields. I made an example how i would solve such a situation, you should be able to use the same technique for your situation.
export default class MyClass extends React.Component{
constructor(props){
super(props);
this.state = {
dynamicItems: []
};
}
handleClick(){
//Add a new item component to state.dynamicItems
this.setState(prevState => ({
dynamicItems: [...prevState.dynamicItems, <Item text="text" key="key" />]
}));
}
render(){
return(
<div className="page">
<div className="dynamic-container">
{/*Render item components*/}
{this.state.dynamicItems.map((item) => {return item})}
</div>
<button onclick={() => this.handleClick()}>Add Item</button>
</div>
);
}
}
//Item component
class Item extends React.Component{
render(){
return(
<div className="item" key={this.props.key}>
<p>{this.props.text}</p>
</div>
);
}
}
I am using react-full-screen library.
Link to code sandbox
I have a navbar, where I have placed the JSX for the button with icons.
class AdminNavbar extends React.Component {
constructor(props) {
super(props);
this.state = {
isFfull: false
};
}
render() {
return (
<Navbar className="navbar" expand="lg">
<Container fluid>
<div className="navbar-wrapper">
<div className="navbar-minimize d-inline">
<button className="btn-fullscreen" onClick={this.props.goFull}>
<i className="fa fa-expand-arrows-alt"></i>
<i className="fa compress-arrows-alt"></i>
</button>
</div>
</div>
</Container>
</Navbar>
);
}
}
And then in my another Admin Component, I am using it as props and performing the onClick()
class Admin extends React.Component {
constructor(props) {
super(props);
this.state = {
isFull: false
};
}
goFull = () => {
if (document.body.classList.contains("btn-fullscreen")) {
this.setState({ isFull: true });
} else {
this.setState({ isFull: false });
}
document.body.classList.toggle("btn-fullscreen");
};
render() {
return (
<Fullscreen
enabled={this.state.isFull}
onChange={(isFull) => this.setState({ isFull })}
>
<div className="wrapper">
<div className="main-panel">
<AdminNavbar {...this.props} goFull={this.goFull} />
</div>
</div>
</Fullscreen>
);
}
}
Problem: the icons are not changing on click of the button. I also tried using the active class. but no luck.
You don't have to check the classList on body. The icon toggle can be achieved by state change.Please have a look at the code.
import React from "react";
import AdminNavbar from "./AdminNavbar";
import Fullscreen from "react-full-screen";
class Admin extends React.Component {
constructor(props) {
super(props);
this.state = {
isFull: false
};
}
goFull = () => {
this.setState({ isFull: !this.state.isFull });
};
render() {
return (
<Fullscreen
enabled={this.state.isFull}
onChange={(isFull) => this.setState({ isFull })}
>
<div className="wrapper">
<div className="main-panel">
<AdminNavbar
{...this.props}
isFull={this.state.isFull}
goFull={this.goFull}
/>
</div>
</div>
</Fullscreen>
);
}
}
export default Admin;
The admin Navbar code
import React from "react";
// reactstrap components
import { Navbar, Container } from "reactstrap";
class AdminNavbar extends React.Component {
constructor(props) {
super(props);
this.state = {
isFfull: false
};
}
render() {
return (
<Navbar className="navbar" expand="lg">
<Container fluid>
<div className="navbar-wrapper">
<div className="navbar-minimize d-inline">
<button className="btn-fullscreen" onClick={this.props.goFull}>
{!this.props.isFull ? (
<i className="fa fa-expand-arrows-alt"></i>
) : (
<i className="fa compress-arrows-alt"></i>
)}
</button>
</div>
</div>
</Container>
</Navbar>
);
}
}
export default AdminNavbar;
I am new to React so looking like how can I render the data of image, name and user name from render method of one component to other components.
class Avatar extends React.Component {
render() {
return (
<img src={''} />
)
}
}
class Label extends React.Component {
render() {
return (
<h1>Name: </h1>
)
}
}
class ScreenName extends React.Component {
render() {
return (
<h3>Username: </h3>
)
}
}
class Badge extends React.Component {
render() {
return (
<div>
<Avatar />
<Label />
<ScreenName />
</div>
)
}
}
And the render method is this. How to read this image username and name into other components and update the view. Tried using {this.props.name} and also {this.props.user.name} but I am getting name as undefined.
ReactDOM.render(
<Badge user={{
name: 'Tyler McGinnis',
img: 'https://avatars0.githubusercontent.com/u/2933430?v=3&s=460',
username: 'tylermcginnis'
}} />,
document.getElementById('app')
);
And the HTML is this
<div id='app'></div>
You pass data via the component's props. It looks like this:
class Avatar extends React.Component {
render() {
return (
<img src={this.props.img} />
)
}
}
class Label extends React.Component {
render() {
return (
<h1>{this.props.name}</h1>
)
}
}
class ScreenName extends React.Component {
render() {
return (
<h3>{this.props.username}</h3>
)
}
}
class Badge extends React.Component {
render() {
return (
<div>
<Avatar img={this.props.user.img}/>
<Label name={this.props.user.name} />
<ScreenName username={this.props.user.username} />
</div>
)
}
}
And after some refactoring, you end up with this:
const Avatar = ({img}) => (
<img src={img} />
);
const Label = ({name}) => (
<h1>{name}</h1>
);
const ScreenName = ({username}) => {
<h3>{username}</h3>
);
const Badge = ({user}) => (
<div>
<Avatar img={user.img}/>
<Label name={user.name} />
<ScreenName username={user.username} />
</div>
)
Note that here we made use of so called functional stateless components, which can make your code a lot shorter and often more elegant. See here.
You can pass the data via props
https://codesandbox.io/s/o4nz576jn5
I am trying to show my results from a JSON file only when the search button is clicked. What is the correct way to do it?
Right now as the user types a product the results are show. I have a simple filter, that is filtering the results, but I would like to make that only appear when the button is clicked. I only want to show results when the search button is clicked.
class App extends Component {
constructor(props){
super(props);
this.state = {
value: '',
list: []
}
this.handleChange = this.handleChange.bind(this);
this.handleSearch = this.handleSearch.bind(this);
this.refresh();
}
handleChange(event){
this.setState({ ...this.state, value: event.target.value })
}
refresh(){
axios.get(`${URL}`)
.then(resp => this.setState({...this.state, value: '', list: resp.data}));
}
handleSearch(product){
this.refresh();
}
render(){
return(
<div className="outer-wrapper">
<Header />
<main>
<Container>
<Row>
<Col xs={12} md={12} lg={12} className="pl-0 pr-0">
<SearchBar
handleChange={this.handleChange}
handleToggle={this.handleToggle}
handleSearch={this.handleSearch}
value={this.state.value}
/>
<SearchResultBar
value={this.state.value}
/>
<Filter />
</Col>
</Row>
<ProductList
value={this.state.value}
list={this.state.list}
/>
</Container>
</main>
</div>
)
}
}
export default App;
class Search extends Component {
constructor(props){
super(props);
}
render(){
return(
<div className="search-input">
<InputGroup>
<Input placeholder='Enter Search'
onChange={this.props.handleChange}
value={this.props.value}
/>
<InputGroupAddon className='input-group-append'
onClick={this.props.handleSearch}>
<span className='input-group-text'>
<i className="fa fa-search fa-lg fa-flip-horizontal"></i>
</span>
</InputGroupAddon>
</InputGroup>
</div>
)
}
}
export default Search;
class ProductList extends Component {
constructor(props){
super(props);
this.state = {
}
}
render(){
let filteredSearch = this.props.list.filter(
(product) => {
return product.title.indexOf(this.props.value) !== -1
}
)
return(
<Container>
<Row>
{
filteredSearch.map(item => {
return <Product {...item} key={item._id} />
})
}
</Row>
</Container>
);
}
}
export default ProductList;
As it stands, my list of products is being displayed in the app as soon as it loads. This seems something trivial, but I have been scratching my head in trying to solve it.
You're calling this.refresh() inside the constructor. So it gets run on mount.
Just remove it from the constructor and you should be fine.
I have 5 such list items i.e self , parents , siblings , relative, friend. Clicking on any item , I am adding a class called active-option . Below is my code , what I have done so far. To note , I am a new to React JS.
import React, { Component } from 'react';
import {Grid, Col, Row, Button} from 'react-bootstrap';
import facebook_login_img from '../../assets/common/facebook-social-login.png';
const profilesCreatedBy = ['Self' , 'Parents' , 'Siblings' , 'Relative' , 'Friend'];
class Register extends Component {
constructor(props) {
super(props);
this.state = { addClass: false };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ addClass: !this.state.addClass });
}
render() {
let selectOption = ["option"];
if (this.state.addClass) {
selectOption.push("active-option");
}
return (
<section className="get-data__block" style={{padding: '80px 0 24px 0'}}>
<Grid>
<Row>
<Col sm={10} md={8} mdOffset={2} smOffset={1}>
<p className="grey-text small-text m-b-32"><i>
STEP 1 OF 6 </i>
</p>
<div className="data__block">
<div className="step-1">
<p className="m-b-32">This profile is being created by</p>
<Row>
{profilesCreatedBy.map((profileCreatedBy, index) => {
return <Col className="col-md-15">
<div onClick={this.handleClick} className={selectOption.join(" ")}>
{profileCreatedBy}
</div>
</Col>;
})}
</Row>
</div>
<Row className="text-center">
<Col xs={12} className="text-center">
<Button href="#" bsStyle="primary" className="m-t-96 m-b-16 has-box__shadow" >
Continue
</Button>
</Col>
</Row>
</div>
</Col>
</Row>
</Grid>
</section>
);
}
}
export default Register;
I am using a map function to display all items. I have tried to add a class called active-option to option. But clicking on any item is adding the class to every other item also. (Attached) Any suggestion ? I want to add active-option class to the one where click event happens, not to every other element. Siblings should not contain active-option class. Please help !
You can achieve this with keeping active item id in the state of component, for example:
class Test extends React.Component{
constructor(){
super();
this.state = {
activeId: null
}
this.setActiveElement = this.setActiveElement.bind(this);
}
setActiveElement(id){
this.setState({activeId: id})
}
render(){
return(
<div>
{
[1,2,3,4,5].map((el, index) =>
<div className={index === this.state.activeId? "active" : ""} onClick={() => this.setActiveElement(index)}>click me</div>
)
}
</div>
)
}
}
https://jsfiddle.net/69z2wepo/85095/