Issue in Rendering Sub-menu component in React Js - javascript

topNavigation.js
import React, { Component } from 'react';
import axios from 'axios';
import SubMenu from './subMenu';
class Navigation extends Component {
state = {
mainCategory: []
}
componentDidMount() {
axios.get('http://localhost:3030/topCategory')
.then(res => {
console.log(res.data.express);
this.setState({
mainCategory: res.data.express.catalogGroupView
})
})
}
render() {
const { mainCategory } = this.state;
return mainCategory.map(navList => {
return (
<ul className="header">
<li key={navList.uniqueID}> <button className="dropbtn ">{navList.name}</button>
<SubMenu below={this.props.navList.catalogGroupView}/>
</li>
</ul>
)
})
}
}
export default Navigation;
I'm new to react and trying to make an ecommerce website. I have designed the homepage. For the navigation, I have used an external api
http://149.129.128.3:3737/search/resources/store/1/categoryview/#top?depthAndLimit=-1,-1,-1,-1
and mapped the response.
If I use the below code in place of
SubMenu component it works
<ul>
{console.log(navList.catalogGroupView)}
{
navList.catalogGroupView.map(sub=> {
return <li key={sub.uniqueID}> <button>{sub.name}</button></li>
})
}
</ul>
But as per the url endpoint response there are more sub categories which I'm unable to map.
I thought of creating a separate component to display the sub menu items. But whenever I use this code it doesn't work.
Submenu.js component
import React, { Component } from 'react';
class SubMenu extends Component {
constructor(props) {
super(props);
this.state = {
subCategory: []
};
}
render() {
return subCategory.map(sub => {
return (
<ul>
<li key={sub.uniqueID}> <button>{sub.name}</button></li>
</ul>
)
})
}
}
export default SubMenu;
Can somebody please help me on this. I would be grateful. I'm not getting where I got wrong.

You're trying to access subCategory of state. Here is the working part:
import React, { Component } from 'react';
class SubMenu extends Component {
constructor(props) {
super(props);
this.state = {
subCategory: []
};
}
render() {
const { subCategory } = this.state; // you should've add this part
return subCategory.map(sub => {
return (
<ul>
<li key={sub.uniqueID}> <button>{sub.name}</button></li>
</ul>
)
})
}
}
export default SubMenu;
UPDATE
Over here:
<SubMenu below={this.props.navList.catalogGroupView}/>
You're trying to access prop, which doesn't exist, so you should do it that way:
<SubMenu below={navList.catalogGroupView}/>
And then In Submenu, you should map over the props, not state, because your state always will be empty array(because you're not setting it). And you don't need to use state in that case, because you just need to iterate over array. So here is the code of Submenu:
import React, { Component } from 'react';
class SubMenu extends Component {
render() {
const {below} = this.props;
return below.map(sub => {
return (
<ul>
<li key={sub.uniqueID}> <button>{sub.name}</button></li>
</ul>
)
})
}
}
export default SubMenu;
And theoretically working code of topNavigation.js:
import React, { Component } from 'react';
import axios from 'axios';
import SubMenu from './subMenu';
class Navigation extends Component {
state = {
mainCategory: []
}
componentDidMount() {
axios.get('http://localhost:3030/topCategory')
.then(res => {
console.log(res.data.express);
this.setState({
mainCategory: res.data.express.catalogGroupView
})
})
}
render() {
const { mainCategory } = this.state;
return mainCategory.map(navList => {
return (
<ul className="header">
<li key={navList.uniqueID}> <button className="dropbtn ">{navList.name}</button>
<SubMenu below={navList.catalogGroupView}/>
</li>
</ul>
)
})
}
}
export default Navigation;
Hope this helps.

Related

how to display a list of items in react from a db

I'm trying to display a list of item from a database, but only those with the Id of the account I'm logged in the application.
I tried to import a previous state with the account and use the id in the api to get only those items, then i use componentWillMount to render the page with the items i selected from the database. I get that i can't use static getDerivedStateFromProps and componentWillMount ("Warning: Unsafe legacy lifecycles will not be called for components using new component APIs.
StoricoView uses getDerivedStateFromProps() but also contains the following legacy lifecycles:
componentWillMount
The above lifecycles should be removed")
import React, { Component } from "react";
import axios from "axios";
import StoricoItems from "./Storicoitems";
import { styles } from "./styles.scss";
import { Form, Label, Input } from "components/Form";
import * as accountActionCreators from "core/actions/actions- account";
import * as contractActionCreators from "core/actions/actions-contract";
import * as assetActionCreators from "core/actions/actions-asset";
import { requestAccountAccess } from "core/libs/lib-metamask-helper";
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { bindActionCreators } from "redux";
class StoricoView extends Component {
constructor(props) {
super(props);
this.state = {
allowToProceed: false,
email: "",
receiverCode: "",
initialDate: "",
expireDate: "",
rights: "",
codiceReferto: null,
nextBtnDisabled: true,
id: "",
storico: []
};
}
componentDidMount() {
const { actions } = this.props;
requestAccountAccess(defaultAccount => {
actions.account.setDefaultAccount(defaultAccount);
actions.contract.setContract(defaultAccount);
});
}
static getDerivedStateFromProps(prevState) {
const { account } = prevState;
return { id: account.id };
}
componentWillMount() {
this.mostraStorico(this.state.id);
}
mostraStorico(id) {
axios.get("http://localhost:8080/api/assegna_consensos/" + id);
then(response => {
console.log(response.data);
this.setState({ storico: response.data }, () => {
console.log(this.state);
});
}).catch(err => console.log(err));
}
render() {
const storicoItems = this.state.storico.map((storico, i) => {
return <StoricoItems key={storico.public_key} item={storico} />;
});
return (
<div>
<h1>Storico</h1>
<ul className="container">{storicoItems}</ul>
</div>
);
}
}
function mapStateToProps(state) {
return {
account: state.account,
asset: state.asset
};
}
function mapDispatchToProps(dispatch) {
return {
actions: {
account: bindActionCreators(accountActionCreators, dispatch),
contract: bindActionCreators(contractActionCreators, dispatch)
}
};
}
StoricoView.propTypes = {
account: PropTypes.shape({
email: PropTypes.string,
receiverCode: PropTypes.string,
initialDate: PropTypes.date,
expireDate: PropTypes.date,
rights: PropTypes.string,
codiceReferto: PropTypes.string,
id: PropTypes.string
}).isRequired,
actions: PropTypes.shape({}).isRequired,
asset: PropTypes.shape({}),
history: PropTypes.shape({}).isRequired
};
StoricoView.defaultProps = {
asset: null
};
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps
)(StoricoView)
);
While StoricoItems is :
import React, { Component } from "react";
import { Link } from "react-router-dom";
class StoricoItem extends Component {
constructor(props) {
super(props);
this.state = {
products: props.products
};
}
render() {
return (
<li className="registration-form">
<ul className="container">
<li className="registration-form">
Proprietario : {this.state.products.giver}
</li>
</ul>
<ul className="container">
<li className="registration-form">
Beneficiario : {this.state.products.receiver}
</li>
</ul>
<ul className="container">
<li className="registration-form">
Data inizio consenso : {this.state.item.data_inizio}
</li>
</ul>
<ul className="container">
<li className="registration-form">
Data Fine Consenso : {this.state.item.data_fine}
</li>
</ul>
<ul className="container">
<li className="registration-form">
{" "}
Tipo consenso : {this.state.item.diritti}
</li>
</ul>
<ul className="container">
<li className="registration-form">
Referto :{this.state.item.referto}
</li>
</ul>
<br />
<br />
</li>
);
}
}
export default StoricoItem;
Of course i'm doing it wrong but what?
First thing you shouldn't call API from componentWillMount and instead move the call to componentDidMount (it's better to wait for the component to be mounted before making api call).
Then you can remove componentWillMount and the message will disappear. Indeed componenentWillMount is depracated in React v16.* and will be removed in React v17.
To explain what happened, since you use componentWillMount and getDerivedStateFromProps which is a replacement for componentWillMount React will prefer use getDerivedStateFromProps and won't call willMount method that's why your api call were never made

Trying to delete an item from arrays of objects in React

I'm learning React. I'm trying to build a simple todo app on my own to learn & practice with the library. I have passed a list of tasks in the parent component & passed them to the child component as props. I was also able to output it in the child component using the map() method. However, I have no idea how to delete an item. I have tried searching online, but I'm unable to adapt their solutions to my use case.
Parent Component
import React, { Component } from 'react';
import './styles/components/App.css';
import Todos from './components/Todos'
class App extends Component {
state = {
todos: [
{ task: 'Study React', id: 1 },
{ task: 'Build something with it', id: 2 },
{ task: 'Apply for jobs', id: 3 },
],
}
render(){
return (
<div className="App">
<Todos todos={this.state.todos} />
</div>
);
}
}
export default App;
Child Component
import React, { Component } from 'react';
import '../styles/components/Todos.css'
class Todos extends Component {
render() {
let { todos } = this.props;
let todoList = todos.map(( todo => {
return (
<div className="todos" key={todo.id}>
<div>{ todo.task }</div>
</div>
)
}));
return (
<div onClick={this.deleteTask}>{ todoList }</div>
)
}
deleteTask() {
// checks if method is working
console.log('working');
// code to delete
}
}
export default Todos
Parent Component
import React, { Component } from 'react';
import './styles/components/App.css';
import Todos from './components/Todos'
class App extends Component {
state = {
todos: [
{ task: 'Study React', id: 1 },
{ task: 'Build something with it', id: 2 },
{ task: 'Apply for jobs', id: 3 },
],
};
// Event and data put in same Component.
deleteTodo(id) {
let workTodos = [...this.state.todos];
const deleteIndex = workTodos.findIndex(todo => todo.id === id);
workTodos.splice(deleteIndex, 1);
this.setState({
todos: [...workTodos]
})
}
render(){
// Use props give Todos Component the data and events to render dom
return (
<div className="App">
<Todos
todos={this.state.todos}
deleteTodo={this.deleteTodo.bind(this)}
/>
</div>
);
}
}
export default App;
Child Component
import React, { Component } from 'react';
import '../styles/components/Todos.css'
class Todos extends Component {
render() {
// Receiving events and data form props
let { todos, deleteTodo } = this.props;
// Click div trigger deleteTodo, It can execute deleteTodo in App component
return todos.map(( todo => {
return (
<div
className="todos"
key={todo.id}
onClick={() => { deleteTodo(todo.id) }}
>
<div>{ todo.task }</div>
</div>
)
}));
}
}
export default Todos
Like a commit, put delete event in App component, Then use props trigger it in the Todos component, Please let me know if you have any questions.

React - Passing state from child to parent in drop down select

I am making a basic dropdown selector. I almost had it working when I realized I was setting the state in both the parent and the child so I refactored again to try to simplify it all and put most of the responsibility in one place.
My logic is in the MyDropDown component, then I have a Header component, then the Main which should render it all.
import React from 'react';
class MyDropdown extends React.Component {
render() {
let initialUsers = this.props.state.users;
let alphabetizeUsers = initialUsers
.sort((a, b) => {
return a.name > b.name;
})
.map(obj => {
return (
<option key={obj.id} value={obj.name}>
{obj.name}
</option>
);
});
return <select>{alphabetizeUsers}</select>;
}
}
export default MyDropdown;
Then I have my main component where I do the api call and pass the state into the dropdown component.
import React from 'react';
import MyDropdown from './MyDropdown';
class UserHeader extends React.Component {
state = {
users: []
};
componentDidMount() {
let initialUsers = [];
fetch('http://localhost:3000/users')
.then(response => {
return response.json();
})
.then(data => {
this.setState({ users: data });
});
}
render() {
return <MyDropdown state={this.state} />;
}
}
export default UserHeader;
And finally my Main Component, where I want to show the value from the selected dropdown menu
import React, { Component } from 'react';
import './Main.css';
import MyDropdown from './components/MyDropdown';
import UserHeader from './components/UserHeader';
class Main extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<span className="App-title">SELECT A USER:</span>
<UserHeader />
</header>
<p className="App-intro">
I should get the dropdown value here: {this.state.user}
</p>
</div>
);
}
}
export default Main;
What I tried doing is moving the statement
I should get the dropdown value here: {this.state.policies} .
into the UserHeader component. How do I get the value selected in the child back up to its parent?
Another thing I've tried is adding a handler to the child component
onChange = e => {
this.setState({ selectedUser: e.target.value });
};
and add it to the select... but again not sure how to get this value up to the parent.
return <select onChange={this.onChange}>{alphabetizeUsers}</select>;
The easiest way to pass the value back to the parent component is through a callback.
Try defining and passing in an onChange={this.onChange} to your Main component like so your Main component becomes:
import React, { Component } from 'react';
import './Main.css';
import MyDropdown from './components/MyDropdown';
import UserHeader from './components/UserHeader';
class Main extends Component {
this.state = {
user: null,
}
constructor(props) {
super(props);
this.onChangeUser = this.onChangeUser.bind(this);
}
onChangeUser(newUser) {
this.setState({ user: newUser });
}
render() {
return (
<div className="App">
<header className="App-header">
<span className="App-title">SELECT A USER:</span>
<UserHeader onChangeUser={this.onChangeUser} />
</header>
<p className="App-intro">
I should get the dropdown value here: {this.state.user}
</p>
</div>
);
}
}
export default Main;
Now you are passing in a callback, you can do the same thing with your UserHeader component.
import React from 'react';
import MyDropdown from './MyDropdown';
class UserHeader extends React.Component {
state = {
users: []
};
componentDidMount() {
let initialUsers = [];
fetch('http://localhost:3000/users')
.then(response => {
return response.json();
})
.then(data => {
this.setState({ users: data });
});
}
render() {
return <MyDropdown state={this.state} onChange={this.props.onChangeUser} />;
}
}
export default UserHeader;
And finally, you can now attach this callback to your <select> element.
import React from 'react';
class MyDropdown extends React.Component {
render() {
let initialUsers = this.props.state.users;
let alphabetizeUsers = initialUsers
.sort((a, b) => {
return a.name > b.name;
})
.map(obj => {
return (
<option key={obj.id} value={obj.name}>
{obj.name}
</option>
);
});
return <select onChange={(ev) => this.props.onChange(ev.target.value)}>{alphabetizeUsers}</select>;
}
}
export default MyDropdown;
By defining the onChange on your select element like this, onChange={(ev) => this.props.onChange(ev.target.value)}, you can return the value to the main component and use it in your state.

How set the state of parent component when i toggle between the links and fetch the data based on tag value

Task is to fetch data from api when toggle between tags
When click on the link it calls the api service but state of feeds is not updated but it throws below warning
jQuery.Deferred exception: Cannot read property 'setState' of undefined TypeError: Cannot read property 'setState' of undefined
My github repo
https://github.com/dolphine4u/demo-app
APP component
import React from 'react';
import {FetchData} from "../service/flickerApi.service";
import Header from "./header/header.component";
import Navigation from "./navigation/navigation.component";
import ProductList from "./products/products.component";
import Footer from "./footer/footer.component";
class App extends React.Component {
constructor() {
super()
this.state = {
feeds: [],
favorites:[]
};
this.addToFavorites = this.addToFavorites.bind(this);
}
handleChange( value ) {
this.setState( { feeds: value })
}
addToFavorites(id) {
const {feeds ,favorites} = this.state;
const findId = feeds.filter(item => {
return item.id === id;
})
favorites.push(findId)
console.log(favorites)
// localStorage.setItem('favorite', JSON.stringify(this.state.favorites));
this.setState({
feeds: favorites
});
}
/* componentWillMount(){
let LoadFeeds = localStorage.getItem('FlickerFeeds');
LoadFeeds && this.setState({
feeds: JSON.parse(LoadFeeds)
})
}*/
componentDidMount() {
FetchData.call(this);
}
/* componentWillUpdate(nextprops, nextState){
localStorage.setItem('FlickerFeeds', JSON.stringify(nextState.feeds))
}
*/
render() {
const {feeds} = this.state;
const productList = feeds.map((item,index) => {
return <ProductList
key={index}
title={item.title}
image={item.src}
id={item.id}
author={item.author}
date={item.created}
update={this.addToFavorites}
/>
})
return ([
<Header key="header"/>,
<Navigation key="navigation" />,
<section key="productList">
<div className="container">
<div className="row row-eq-height">
{productList}
</div>
</div>
</section>,
<Footer key="footer"/>
]);
}
}
export default App;
Navigation component
import React from 'react';
import Link from "./link.component";
import './navigation.css';
class Navigation extends React.Component {
constructor(props) {
super(props)
this.state = {
tags: [
{tag:"kittens"},
{tag:"dogs"},
{tag:"lion"},
{tag:"tiger"},
{tag:"leapord"}]
};
}
render() {
const {tags} = this.state;
const tagList = tags.map(item => {
return <Link
key={item.tag}
tag={item.tag}
/>
})
return (
<nav className="nav">
<div className="container">
<ul className="nav-bar">
{tagList}
</ul>
</div>
</nav>
);
}
}
export default Navigation;
Link Component
import React from 'react';
import {FetchData} from "../../service/flickerApi.service";
class Link extends React.Component {
constructor(props) {
super(props)
this.onClick = this.onClick.bind(this);
}
onClick(e) {
FetchData(this.props.tag);
}
render() {
return (
<li><a href="#" onClick={this.onClick}>{this.props.tag}</a></li>
);
}
}
export default Link;
product component
import React from 'react';
import './product.css';
class ProductList extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick(e) {
this.props.update(this.props.id);
}
render() {
return (
<div className="product-column">
<div className="product-item">
<div className="product-content">
<div className="product-author">
<strong>Author: </strong>{this.props.author}
</div>
{/*<div className="product-image" style={{backgroundImage: "url(" + this.props.image + ")"}}/>*/}
</div>
<div className="product-content">
<div className="product-date">
Created Date: {this.props.date}
</div>
<h3 className="product-title">{this.props.title}</h3>
<button className="product-btn" onClick={this.onClick}>
Add to Favourites
</button>
</div>
</div>
{/*<div className="product-description" dangerouslySetInnerHTML={{__html: this.props.description}}>
</div>*/}
</div>
);
}
}
export default ProductList;
Api service
import $ from "jquery";
import {getLastPartOfUrl, formatDate, removeUrl, getString} from "../helpers/helper";
export function FetchData(tag) {
const URL = "https://api.flickr.com/services/feeds/photos_public.gne?format=json&jsoncallback=?"
const SUFFIX_SMALL_240 = "_m";
const SUFFIX_SMALL_320 = "_n";
$.getJSON({
url : URL,
data: {
tags: tag
}
})
.then(response => {
let list= response.items.map(item => ({
title: removeUrl(item.title),
id: getLastPartOfUrl(item.link),
description: item.description,
link: item.link,
src: item.media.m.replace(SUFFIX_SMALL_240, SUFFIX_SMALL_320),
author: getString(item.author),
created: formatDate(item.published),
tags: item.tags,
fav: false
}));
this.setState({
feeds: list
})
}).catch(function(error){
console.log(error);
});
}
You're trying to call this.addToFavorites from a click handler that is not even bound to this. I think two changes are needed for this to work:
In App component, change the addFavorites function to an arrow function so it gets the context this:
addToFavorites = id => {
...
Same in ProductList component for the click handler:
onClick = () => {
this.props.update(this.props.id);
}

Using .map() to render Component UI with information from API

I'm currently having a problem trying to get UI to render with React. I'm using information I've received from ShopifyAPI and trying to render it to my component. I'm not sure what to do. Do I need to update the state with information returned from my API? Here's my code at the moment.
ShopifyCatalog.js
import React, { Component } from 'react';
import { Link } from 'react-router'
import styles from '../styles';
import ShopProducts from './ShopProducts'
import { getAllProducts } from '../utils/shopifyHelpers';
export default class ShopCatalog extends Component {
constructor(...args){
super(...args);
this.state = {
allProducts: []
}
}
render() {
let allProducts
getAllProducts()
.then((products) => {
return allProducts = products
})
.then((allProducts) => {
allProducts.map((product) => {
<div className='col-sm-offset-1 col-sm-2'>
<Link to={'shop/${product.id}'}>
<img src={product.images[0].src} />
<h5>{product.title}</h5>
</Link>
</div>
})
})
return (
<div style={styles.productInfo}>
{allProducts}
</div>
)
}
}
I thought it might have something to do with using promises more extensively, but I'm pretty sure it's because my state isn't updating with the information that I'm grabbing from the API. I appreciate your time, thank you.
EDIT:
I've updated my code now and it looks like this
ShopCatalog.js Updated
import React, { Component } from 'react';
import { Link } from 'react-router'
import styles from '../styles';
import ShopProducts from './ShopProducts'
import { getAllProducts } from '../utils/shopifyHelpers';
export default class ShopCatalog extends Component {
constructor(...args){
super(...args);
this.state = {
allProducts: [],
listAllProducts: []
}
}
componentDidMount() {
getAllProducts()
.then((products) => {
this.setState({
allProducts: products
})
})
}
render() {
return (
<div style={styles.productInfo}>
{this.state.allProducts.map((product) => {
<h1>{product.title}</h1>
})}
</div>
)
}
}
But it's still not rendering anything from the map of my state. Is it because map is called while there is nothing in the state? How do I work around this so map get's called and returns UI? Thank you.
Put your request in the componentDidMount lifecycle method, then update your state. Your render method is returning before your request has completed.
export default class ShopCatalog extends Component {
constructor(...args){
super(...args);
this.state = {
allProducts: []
}
}
componentDidMount() {
const _this = this;
getAllProducts()
.then((products) => {
_this.setState({ allProducts: products });
});
}
render() {
return (
<div style={styles.productInfo}>
{this.state.allProducts.map((product) => {
<div className='col-sm-offset-1 col-sm-2'>
<Link to={'shop/${product.id}'}>
<img src={product.images[0].src} />
<h5>{product.title}</h5>
</Link>
</div>
})}
</div>
)
}
}
I assume something like this, not sure specifics to your case, just giving idea how this should look like.
export default class ShopCatalog extends Component {
state = {
allProducts: []
}
getAllProducts = () => {
fetch(...API).then(response => response.json()).then(products =>
this.setState({allProducts: products}));
}
componentDidMount() {
this.getAllProducts()
}
render() {
const {allProducts} = this.state;
return (
<div>
{allProducts.map((product,key) => <div key={key}>
<span>{product.title}</span>
</div>
)}
</div>
)
}
}

Categories

Resources