I'm newbie to programming and trying to build React App with REST API. Currently I retrieve and pass data from parent to child components by having two fetchData function in parent component since they have different API parameter. However, I'm doubt that this should be the right way as I still have more child components to build. It also has problem showing the data when refreshing a component page, I should navigate on routes before it shows. Please advise or link me a sample method on how should I handle my case.
Parent:
import React, {Component} from 'react';
import {BrowserRouter as Router, Route, Link} from 'react-router-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import {NavDropdown, Nav, Navbar} from 'react-bootstrap';
import axios from 'axios';
import * as constants from './components/constants/Constants'
import Home from './components/dashboard/home';
import AddCategories from './components/category/AddCategories';
import ViewCategories from './components/category/ViewCategories';
import AddAssets from './components/assets/AddAssets';
import ViewAssets from './components/assets/ViewAssets';
import './App.css';
class App extends Component{
constructor(props){
super(props);
this.state = {
assetItems:[],
categoryItems:[],
items : [],
currentPage: 1,
totalPages: 1,
totalItems: 1,
}
}
getAssetAssetItems=()=>{
this.setState({
currentPage: 1,
});
axios.get(constants.apiURL + 'Assets?&ShowAll=false')
.then(
(result)=>{
this.setState({assetItems: result.data.list, totalPages: result.data.totalPage, totalItems: result.data.total})
})
.catch(error =>console.log(error));
}
getCategoryItems=()=>{
axios.get(constants.apiURL + 'Categories?&ShowAll=false')
.then(
(result)=>{
this.setState({categoryItems: result.data.list, totalPages: result.data.totalPage, totalItems: result.data.total})
console.log(this.state.totalPages)
console.log(this.state.currentPage)
})
.catch(error =>console.log(error));
}
componentDidMount=()=>{
this.getAssetAssetItems();
this.getCategoryItems();
}
render(){
return(
<Router>
<div>
<Navbar bg="light" expand="lg">
<Navbar.Brand as={Link} to='/'>Asset Management</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
<Nav.Link as={Link} to='/Home'>Home</Nav.Link>
<Nav.Link as={Link} to='/Assets'>Assets</Nav.Link>
<NavDropdown title="Manage" id="basic-nav-dropdown">
<NavDropdown.Item as={Link} to='/manage/category'>Categories</NavDropdown.Item>
<NavDropdown.Divider />
<NavDropdown.Item as={Link} to='/manage/manufacturers'>Manufacturers</NavDropdown.Item>
<NavDropdown.Item href="#action/3.3">Models</NavDropdown.Item>
<NavDropdown.Item href="#action/3.4">Processors</NavDropdown.Item>
<NavDropdown.Item href="#action/3.5">Suppliers</NavDropdown.Item>
<NavDropdown.Divider />
<NavDropdown.Item>Sizes</NavDropdown.Item>
<NavDropdown.Item href="#action/3.6"> Hard Disk</NavDropdown.Item>
<NavDropdown.Item href="#action/3.7"> Memory</NavDropdown.Item>
<NavDropdown.Item href="#action/3.8"> Video Card</NavDropdown.Item>
</NavDropdown>
</Nav>
<Nav>
<Nav.Link href="users">Users</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
<Route path='/Home' exact component={Home}/>
<Route path='/Assets'>
<AddAssets/>
<ViewAssets assetItems={this.state.assetItems}/>
</Route>
<Route path='/manage/category'>
<AddCategories/>
<ViewCategories categoryItems={this.state.categoryItems} totalPages={this.state.totalPages}/>
</Route>
</div>
</Router>
)
}
}
export default App;
Child 1:
import React, {Component} from 'react';
import {Button, Table} from 'react-bootstrap';
import axios from 'axios';
class ViewAssets extends Component{
constructor(props){
super(props);
this.state = {
assetItems : this.props.assetItems,
assetKeys : [],
currentPage: 1,
totalPage: 1,
totalItems: 1,
}
}
generateColumnHeader(){
return(
<tr >
<th>ID</th>
<th>Serial No.</th>
<th>Asset Tag</th>
<th>Host Name</th>
<th>Assigned To</th>
<th>Status Type</th>
<th>Manufacturer Name</th>
<th>Category Name</th>
<th>Purchase Date</th>
<th>Warranty</th>
<th>Actions</th>
</tr>
);
}
generateRowsData=()=>{
return(
this.state.assetItems.map(assets=>{
return(
<tr key={assets.id}>
<td>{assets.id}</td>
<td>{assets.serialNo}</td>
<td>{assets.assetTag}</td>
<td>{assets.name}</td>
<td>{assets.assignedTo}</td>
<td>{assets.statusType}</td>
<td>{assets.manufacturerName}</td>
<td>{assets.categoryName}</td>
<td>{assets.purchaseDate}</td>
<td>{assets.warranty}</td>
<td>{this.generateActionButtonInfo()}{this.generateActionButtonEdit()}</td>
</tr>
);
})
)
}
generateActionButtonInfo=()=>{
return(
<Button type="submit" value ="Info" variant="info" onClick={this.showInfo}>Info</Button>
)
}
generateActionButtonEdit=()=>{
return(
<Button type="submit" value ="addCategory" variant="secondary" onClick={this.handleEdit}>Edit</Button>
)
}
generateAssetTable(){
return(
<Table striped bordered hover>
<thead>{this.generateColumnHeader()}</thead>
<tbody>{this.generateRowsData()}</tbody>
</Table>
)
}
render(){
return(
<div>
<div className="container">
<div className="d-flex justify-content-center">
{this.generateAssetTable()}
</div>
</div>
</div>
);
}
}
export default ViewAssets;
You can make api calls inside the success callback of the previous api call , and in the final success callback, you can do a single setstate. You can also show the progress percentage using axios's onUploadProgress / onDownloadProgress .
For the final touch , you can have an error page if anything goes wrong , and render that if any issues during the api calls.
Related
I'm trying a double nested for loop but it's not quite unraveling how I predict, advice? am i not allowed to call a double for loop in function? Essentially it suppose to create a navbar from an array of objects i.e [{name:['Jonathan,'bob']},{dob:['may 21','june 22']}]
import React,{Component} from "react";
import Container from 'react-bootstrap/Container';
import Nav from 'react-bootstrap/Nav';
import Navbar from 'react-bootstrap/Navbar';
import NavDropdown from 'react-bootstrap/NavDropdown';
export function navBar(content){
return(
<Navbar bg="light" expand="lg">
<Container>
<Navbar.Brand href="#home">React-Bootstrap</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
{content.map((cont) => {
<NavDropdown title="Dropdown" id={Object.keys(cont)[0]}>
{
Object.values(cont).map((key)=>{<a href={"http://localhost:3000/"+key}>{key}</a>})
}
</NavDropdown>
})}
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
);
}
You are not returning from your map functions:
<Nav className='me-auto'>
{content.map((cont) => {
// from here
<NavDropdown title='Dropdown' id={Object.keys(cont)[0]}>
{Object.values(cont).map((key) => {
// and from here
<a href={'http://localhost:3000/' + key}>{key}</a>;
})}
</NavDropdown>;
})}
</Nav>
Solution:
<Nav className='me-auto'>
{content.map((cont) => {
return (<NavDropdown title='Dropdown' id={Object.keys(cont)[0]}>
{Object.values(cont).map((key) => {
return <a href={'http://localhost:3000/' + key}>{key}</a>;
})}
</NavDropdown>);
})}
</Nav>;
Since you are using arrow functions, you can omit the curly brackets and the return:
<Nav className='me-auto'>
{content.map((cont) => (
<NavDropdown title='Dropdown' id={Object.keys(cont)[0]}>
{Object.values(cont).map((key) => (
<a href={'http://localhost:3000/' + key}>{key}</a>
))}
</NavDropdown>
))}
</Nav>;
Also, the content parameter of your navBar is not an array but the props object, so you shouldn't be able to call map on it.
If you use navBar like: <navBar content={...}/>, you have to update its code like:
export function navBar({ content }){
// ...
}
I changed my NavBarDesktop to add props in super like below. My HomePage shows but if I click on About Us or any other page, I get a blank page. I also added my HomePage Code below as well. I am not sure why it does this. If I take const MyView our the pages work but there is no categories in NavDropdown.
NavMenuDesktop
class NavMenuDesktop extends React.Component{
constructor(props){
super(props);
}
render(){
const CatList = this.props.data;
const MyView = CatList.map((CatList,i)=>{
return <div key={i.toString()}>
<NavDropdown.Item href="">{CatList.category_name}</NavDropdown.Item>
</div>
});
return(
<Fragment>
<Navbar bg="light" expand="lg" sticky="top">
<Container>
<Navbar.Brand href="/"><img src={Logo} className="nav-logo" /></Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" className="nav-toggler" ><i className="fa fa-bars"></i></Navbar.Toggle>
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<Nav.Link href="/">Home</Nav.Link>
<Nav.Link href="/about">About Us</Nav.Link>
<NavDropdown title="Shop" id="basic-nav-dropdown" renderMenuOnMount={true}>
{MyView}
</NavDropdown>
<Nav.Link href="/contact">Contact</Nav.Link>
<NavDropdown title="My Account" id="collasible-nav-dropdown" renderMenuOnMount={true}>
<NavDropdown.Item href="/login"><i className="fa fa-sign-in-alt p-2 colorred"></i>Login</NavDropdown.Item>
<NavDropdown.Item href="#action/3.2"><i className="fa fa-user-plus p-2 colorred"></i>Register</NavDropdown.Item>
</NavDropdown>
<ul className="inline justify-content-right">
<li><Nav.Link href="/favorites"><i className="fa fa-heart fa-md text-secondary"></i>
<sup><span className="badge text-white bgred fa-md">5</span></sup>
</Nav.Link></li>
<li><Nav.Link href="/cart"><i className="fa fa-shopping-cart fa-md text-secondary"></i>
<sup><span className="badge text-white bgred fa-md">5</span></sup>
</Nav.Link>
</li>
</ul>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
</Fragment>
)
}
}
export default NavMenuDesktop;
HomePage
import React, { Component, Fragment } from 'react';
import { Container } from 'react-bootstrap';
import AppURL from '../api/AppURL';
import AppRoute from '../route/AppRoute';
import FeaturedProducts from '../components/home/FeaturedProducts';
import Categories from '../components/home/Categories';
import Collection from '../components/home/Collection';
import NewArrival from '../components/home/NewArrival';
import HomeTop from '../components/home/HomeTop';
import NavMenuDesktop from '../components/common/NavMenuDesktop';
import FooterDesktop from '../components/common/FooterDesktop'
import axios from 'axios';
class HomePage extends Component {
constructor(){
super();
this.state ={
MenuData:[]
}
}
componentDidMount(){
window.scroll(0,0);
this.GetVisitorDetails();
axios.get(AppURL.CategoryInfo).then(response =>{
this.setState({MenuData:response.data});
}).catch(error=>{
});
}
GetVisitorDetails = ()=>{
axios.get(AppURL.VisitorDetails).then().catch()
}
render() {
return (
<Fragment>
<NavMenuDesktop data={this.state.MenuData}/>
<HomeTop />
<Container fluid={"true"}>
<Categories />
<NewArrival />
<FeaturedProducts />
<Collection />
<FooterDesktop/>
</Container>
</Fragment>
)
}
}
export default HomePage;
in constructor use super(props) then write the myView const in componentDidMount() function like this:
componentDidMount(){
const CatList = this.props.data;
const MyView = CatList.map((CatList,i)=>{
return <div key={i.toString()}>
<NavDropdown.Item href="">{CatList.category_name}</NavDropdown.Item>
</div>
});
window.scroll(0,0);
this.GetVisitorDetails();
axios.get(AppURL.CategoryInfo).then(response =>{
this.setState({MenuData:response.data});
}).catch(error=>{
});
}
I want to create an SQL Editor. For that I need to display to which database I am currently connected to.
If I select some Database from the Dropdown Menu the Dropdown component will disappear completely.
Btw: I use the bootstrap library for Dropdown Menus
Here is what I got so far:
App.js
import Header from "./components/Header";
import Switch from "react-bootstrap/Switch";
import {Route, useParams} from "react-router";
import Navbar from "./components/Navbar";
import {useState} from "react";
import QueryPage from "./components/QueryPage";
import ChangeDB from "./components/essentials/ChangeDB";
function App() {
const [connections, setConnections] = useState([
{
id: 1,
dbname: "db1"
},
{
id: 2,
dbname: "db2"
},
{
id: 3,
dbname: "db3"
}]
);
const [db, setDB] = useState(1);
const onDBChange = ({id}) => {
setDB(() => id)
}
return (
<div className="App">
<Header database={db} connections={connections}/>
<Switch>
<Route exact path={["/", "/home","db/:id" ]}>
<QueryPage />
</Route>
<Route path="/db/:id"><ChangeDB callback={onDBChange} /></Route>
</Switch>
</div>
);
}
export default App;
Header.js
import {Button, Form, FormControl, Nav, Navbar, NavDropdown} from "react-bootstrap";
import "./Header.css";
import { Link } from 'react-router-dom';
function Header({ database , connections}) {
alert(database);
return (
<Navbar bg="light" expand="lg">
<Logo />
<Navbar.Brand href="#home">fnmSQL Client</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
<Nav.Link href="#home">Home</Nav.Link>
<Nav.Link href="#link">Link</Nav.Link>
{connections.map(con => {
if(con.id === database) {
return (
<NavDropdown title={con.dbname} id="basic-nav-dropdown">
{connections.length !== 0 ?
connections.filter(id => (database !== id)).map(con => (
<NavDropdown.Item className={"bootstrap-overrides"} href={"#db/" + con.id}>{con.dbname}</NavDropdown.Item>
))
:
<NavDropdown.Item disabled="true">No Database Connections</NavDropdown.Item>
}
</NavDropdown>
)
}
})}
</Nav>
<Form inline>
<FormControl type="text" placeholder="Search" className="mr-sm-2" />
<Button variant="outline-success">Search</Button>
</Form>
</Navbar.Collapse>
</Navbar>
);
}
const Logo = () => {
return (
<Link to="./">
<img src={LogoWhite} alt={"ADVA Optical Networking SE"} className={"img"}/>
</Link>
)
}
export default Header;
I havent found another solution to read from HashRouter than putting it in some extra Component:
ChangeDB.js
function ChangeDB({callback}) {
let id = useParams();
callback(id);
return null;
}
export default ChangeDB;
Found a solution. It was because of the strict equality operator.
It seems that either database or con.id is parsed as a string instead of an integer.
I don't know why maybe somebody got an explanation...
Cheers guys
i have 2 components & i want to pass data by input from parent component to the child component, i got stuck to pass the data to the child component and display or render the child component
is there any technique to pass the props to the child components & render it?
and when i see the console on developer tools there is nothing wrong/happen except the console.log code
import React, {Component} from 'react';
import ReactDOM from'react-dom';
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
//props.keyword
const keyword = 'avenger';
const API = `https://api.themoviedb.org/3/search/movie?api_key=341c549444f65b6a022eea5fc24f5b77&language=en-US&query=${keyword}&page=1&include_adult=false`;
const DEFAULT_QUERY = 'redux';
class MovieSearch extends Component{
constructor(props){
super(props);
this.state={
movies:[]
}
}
componentDidMount(){
fetch(API + DEFAULT_QUERY)
.then(response=>response.json())
.then(data=>{
this.setState({movies:data.results})
})
}
render(){
const {movies} = this.state;
return(
<div className="row container">
{movies.map(movie =>
<div className="col-md-4">
<div className="card" style={{width: '15rem'}} key={movie.id}>
<img src="" className="card-img-top" alt="..."/>
<div className="card-body">
<h5 className="card-title">{movie.title}</h5>
<p>{movie.id}</p>
<Link to="/movie/detail">Detail</Link>
</div>
</div>
</div>
)}
</div>
)
}
}
export default MovieSearch;
import React, { Component } from"react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
//component
import Jumbotron from "./jumbotron";
import MovieList from "./movieList";
import MovieSearch from"./MovieSearch";
import Movie from"./Movie";
//HOC
const idMovie = (WrappedComponent)=>{
class IdMovie extends Component{
constructor(props){
super(props)
this.state={}
}
}
}
//route start here
class Main extends Component{
constructor(props){
super(props)
this.state={
keyword: ''
}
this.handleInput = this.handleInput.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleInput(event){
this.setState({keyword:event.target.value})
}
handleSubmit(event){
console.log(this.state.keyword)
event.preventDefault()
}
render(){
return (
<Router>
<nav className="navbar navbar-expand-lg navbar-dark bg-dark">
<Link className="navbar-brand" to="/">Carifilm</Link>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav mr-auto">
<li className="nav-item active">
<Link className="nav-link" to="/">Home <span className="sr-only">(current)</span></Link>
</li>
<li className="nav-item">
<Link className="nav-link" to="/movies">Movies</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to="/search-movie">Cari film</Link>
</li>
</ul>
<form className="form-inline my-2 my-lg-0" onSubmit={this.handleSubmit}>
<input className="form-control mr-sm-2"
type="search" placeholder="Search"
aria-label="Search"
value={this.state.keyword}
onChange={this.handleInput}/>
<button className="btn btn-outline-success my-2 my-sm-0" type="submit" value="submit">Search</button>
</form>
</div>
</nav>
<Switch>
<Route exact path="/">
<Home/>
</Route>
<Route path="/movies">
<Movies/>
</Route>
<Route path="/movie/detail">
<MovieDetail/>
</Route>
<Route path="/search-movie">
<CariFilm/>
</Route>
</Switch>
</Router>
)}
}
export default Main;
function Home(){
return(
<Jumbotron/>
)
}
function Movies(){
return(
<MovieList/>
)
}
function MovieDetail(){
return(
<Movie/>
)
}
function CariFilm(props){
return(
<MovieSearch/>
)
}
Please check this example. Here I passed items into my Child Component and displayed items in child component.
Parent
import React, {Component, useEffect, useState} from 'react';
import {PChild} from "./PChild";
export class Parent extends Component {
constructor(props) {
super(props);
this.state = {items: []};
}
componentDidMount() {
let json = [];
json.push({track: { id:1, name: 'Black Sabbath, from the album Black Sabbath (1970)'}});
json.push({track: { id:2, name: 'Blackfield, from the album Blackfield (2004)'}});
json.push({track: { id:3, name: 'Bo Diddley, from the album Bo Diddley (1958)'}});
json.push({track: { id:4, name: 'Damn Yankees, from the album Damn Yankees (1990)'}});
this.setState({items: json});
}
render() {
return (
<div>
<PChild items={this.state.items} name="Khabir"/>
</div>
);
}
}
Child
import React, {useEffect, useState} from 'react';
// Parent to Child communication
export class PChild extends React.Component {
componentDidUpdate() {
console.log(this.props.items);
console.log(this.props.name);
}
render() {
return (
<div>
{this.props.items.map((item, i) => {
return <li key={item.track.id}>
{(`Item ${i+1} - ${item.track.name}`)}
</li>
})}
</div>
);
}
}
I am new to React. I would like to redirect to another page (another component) called MainModule from the login form after clicking the submit button.
This is my Login Form=>
import React, {Component} from "react";
import { withRouter} from 'react-router-dom';
import {Button,FormGroup,FormControl,FormLabel,Form} from "react-bootstrap";
class Login extends Component {
constructor(props){
super(props);
this.state={
email:"",
password:""
};
}
handelChange = event =>{
this.setState({
[event.target.id]:event.target.value
},()=>{
});
}
handelSubmit = event => {
event.preventDefault();
this.props.history.push('/MainModule');
}
render()
{
return (
<div className="Login">
<Form onSubmit={this.handelSubmit}>
<FormGroup controlId="email">
<FormLabel>Email</FormLabel>
<FormControl autoFocus type="email" value={this.state.email} onChange={this.handelChange}/>
</FormGroup>
<FormGroup controlId="password">
<FormLabel>Password</FormLabel>
<FormControl type="password" value={this.state.password} onChange={this.handelChange}/>
</FormGroup>
<Button type="submit">Login</Button>
</Form>
</div>
)
}
}
export default withRouter(Login);
but the problem is after I clicking the submit button, url change to MainModule but MainMoudle form is not shown and just showing the current login form.I think this needs to defind the route for MainModule and I don't know how to do that. Please help.
Update
My MainModules =>
import React from 'react';
import { Route, Link,Switch,BrowserRouter } from "react-router-dom";
import allowance from './components/master/allowance';
class MainModule extends React.Component {
// constructor(props){
// super(props)
// }
render(){
return(
<div className="mainform">
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<Link className="navbar-brand" to="/MainModule">TMS</Link>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNavAltMarkup">
<div className="navbar-nav">
<Link className="nav-item nav-link" to={'/allowance'}>Allowance</Link>
</div>
</div>
</nav>
<div id="maincontent">
<Route path='/allowance' component={allowance} />
</div>
</div>
)
}
}
export default MainModule;
You are correct, you do have to define a route for your MainModule component.
In your App.js file, which is where I'm assuming you have set up your Routes:
import React from "react"
import { BrowserRouter, Route } from "react-router-dom"
import Login from "/yourcomponentfolder/Login"
import MainModule from "/yourcomponentfolder/MainModule"
const App = () => {
return(
<BrowserRouter>
<div>
//Using exact tells react-router that you will only render this component if the URL matches exactly with the path definition.
<Route path="/" component={Login} exact/>
<Route path="/MainModule" component={MainModule}/>
</div>
</BrowserRouter>
)
}
It's pretty cut and dry, you use the Route component, give it a path, and a component to render.