I just learn react, and I try make a website with Bootstrap.
Suppose below picture is my web page.
When I browse a link that path is "/project/react" the top left (value of Navbar title is "React"), then I browse a link that path is "/project/bootstrap" I hope the top left (value of Navbar title is "Bootstrap"). Is that possible?
Because I got the title still show "React".
Thank you
//MyPage.js
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/css/bootstrap.css';
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';
import MyNavbar from '../components/MyNavbar';
import MyReact from '../components/MyReact';
import MyBootstrap from '../components/MyBootstrap';
let pgName = "My Page"
class MyPage extends Component {
constructor(props) {
super(props);
this.state = {
pageName: pgName
};
}
setPageName = (pageName) => {
pgName = pageName;
}
componentDidMount(){
this.setState({ pageName: pgName });
}
render() {
return (
<div>
<MyNavbar pageTitle={this.state.pageName}/>
<Switch>
<Route path="/project/bootstrap" component={MyBootstrap} render={this.setPageName("Bootstrap")}/>
<Route path="/project/react" component={MyReact} render={this.setPageName("React")}/>
</Switch>
</div>
)
}
};
export default ProjectPage;
//MyNavbar.js
import React from 'react';
import Navbar from 'react-bootstrap/Navbar';
import Nav from 'react-bootstrap/Nav';
import Scrollchor from 'react-scrollchor';
class MyNavbar extends React.Component {
constructor(props) {
super(props);
this.state = {
pageTitle: props.pageTitle
};
}
componentWillReceiveProps(props,state){
this.setState({ pageTitle: props.pageTitle });
}
render() {
return (
<Navbar bg="dark " expand="lg" fixed="top">
<Navbar.Brand href="#home" className="menu-title">{this.state.projectTitle}</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" position="absolute" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="ml-auto" >
<Scrollchor to="#docs" animate={{ offset: -90, duration: 300 }} className="nav-link">Docs</Scrollchor>
<Scrollchor to="#tutorial" animate={{ offset: -90, duration: 300 }} className="nav-link">Tutorial</Scrollchor>
<Scrollchor to="#blog" animate={{ offset: -90, duration: 300 }} className="nav-link">Blog</Scrollchor>
</Nav>
</Navbar.Collapse>
</Navbar>
);
}
}
export default MyNavbar;
//MyReact.js
import Container from 'react-bootstrap/Container';
import React from 'react';
class MyReact extends React.Component {
render() {
return (
<h1>React</h1>
);
}
}
export default MyReact;
//MyBootstrap.js
import Container from 'react-bootstrap/Container';
import React from 'react';
class MyBootstrap extends React.Component {
render() {
return (
<h1>Bootstrap</h1>
);
}
}
export default MyBootstrap;
You could do something like this.
Pass a callback to the component as a prop
<Route
path="/project/bootstrap"
render={(props) => <MyBootstrap {...props} isAuthed={true} setPageName={this.setPageName.bind(this)} />}
/>
Call the callback when the component is mounted
import Container from 'react-bootstrap/Container';
import React from 'react';
class MyBootstrap extends React.Component {
componentDidMount() {
const { setPageName } = this.props;
if(typeof setPageName === 'function) {
setPageName('Bootstrap');
}
}
render() {
return (
<h1>Bootstrap</h1>
);
}
}
Why calling setPageName in render prop?
according to react document, a render prop is a function prop that a component uses to know what to render.
In this case no need to use render prop just need an onClick function because you just want to change the title of previous rendered Navbar.
Second remember to add setState in your setPageName function, unless DOM would not be aware of state change neither the corresponding props.
Then when you change the pageName state navbar props will react to the change.
<Route path="/project/react" component={MyReact} onClick={() => setPageName (someValue)} />
setPageName = (newPageName) =>{ setState({pageName : newPageName }) }
wish it will help
This may not be correct and is untested, but have you tried this?:
<Route path="/project/bootstrap" component={MyBootstrap} render={() => this.setPageName("Bootstrap")}/>
<Route path="/project/react" component={MyReact} render={() => this.setPageName("React")}/>
I think this is more likely to work, that way your setPageName function is only running on render.
Related
I have a Smart component class page PhoneDirectory.js, where I have used BrowserRouter and Route to route to the ShowSubscribers page("/") and AddSubscribers page ("/add"). My requirement is to redirect to the ShowSubscribers page("/") upon form submission on the AddSubscribers page but not understand how to implement that. I tried using this.props.history.push("/") but it isn't working. I am new to React, can anyone please help?
import React from 'react';
import AddSubscriber from './AddSubscriber';
import ShowSubscribers from './ShowSubscribers';
import {BrowserRouter , Route, Routes} from 'react-router-dom';
export default class PhoneDirectory extends React.Component{
constructor(){
super();
this.state = {[]};
}
addSubscribers = (subscribers) =>{...}
deleteSubscribers = (subscriberId) =>{...}
render() {
return (
<BrowserRouter>
<Routes>
<Route exact path='/' element={<ShowSubscribers deleteSubscribers={this.deleteSubscribers} subscribersList={this.state.subscribersList}/>}/>
<Route exact path='/add' element={<AddSubscriber addSubscribers={this.addSubscribers}/>}/>
</Routes>
</BrowserRouter>
)
}
}
Dumb component AddSubscriber page
import React from 'react';
import Header from './Header';
import './AddSubscriber.css';
import {Link} from "react-router-dom";
export default class AddSubscriber extends React.Component {
constructor() {
super();
this.state = {
id: 0,
name:'',
phone:''
}
}
onChangeHandler = (event) =>{...}
onFormSubmitted = (event) =>{
event.preventDefault();
this.props.addSubscribers(this.state);
this.setState({id:0, name:"", phone:''});
// Need logic to redirect to "/" i.e., ShowSubscribers page
}
render() {
return (<div>
<Header heading="Add Subscriber"/>
<div className="component-body-container">
<Link to="/">
<button className="custom-btn">Back</button>
</Link>
<form className="subscriber-form" onSubmit={this.onFormSubmitted.bind(this)}>
...............
<button type="submit" className="custom-btn add-btn">Add</button>
</div>
</form>
</div>
</div>)}
}
I tried this.props.history.push("/"), but it didn't worked.
react-router-dom v6 support only hooks version i suggest since you use class component to downgrade the version of react-router-dom in package.json.
"dependencies": {
...
"react-router-dom": "5.2.1",
},
Now you need to run : npm install or yarn install
Your Route component will look like that
import React from 'react';
import AddSubscriber from './AddSubscriber';
import ShowSubscribers from './ShowSubscribers';
import { Route, Switch, BrowserRouter as Router } from 'react-router-dom';
export default class PhoneDirectory extends React.Component{
constructor(){
super();
this.state = {[]};
}
addSubscribers = (subscribers) =>{...}
deleteSubscribers = (subscriberId) =>{...}
render() {
return (
<Router>
<Switch>
<Route exact path='/'>
<ShowSubscribers {...your props goes here}/>
</Route>
<Route exact path='/add'>
<AddSubscriber {...your props goes here}/>
</Route>
</Switch>
</Router>
)
}
}
Now you are able to call this.props.history.push
import React from 'react';
import Header from './Header';
import './AddSubscriber.css';
export default class AddSubscriber extends React.Component {
constructor() {
super();
this.state = {
id: 0,
name:'',
phone:''
}
}
onChangeHandler = (event) =>{...}
onFormSubmitted = (event) =>{
event.preventDefault();
this.props.addSubscribers(this.state);
this.setState({id:0, name:"", phone:''});
// Need logic to redirect to "/" i.e., ShowSubscribers page
}
render() {
return (<div>
<Header heading="Add Subscriber"/>
<div className="component-body-container">
<button className="custom-btn" onClick={()=>this.props.history.push('/')} >Back</button>
<form className="subscriber-form" onSubmit={this.onFormSubmitted.bind(this)}>
...............
<button type="submit" className="custom-btn add-btn">Add</button>
</div>
</form>
</div>
</div>)}
}
I think that you need to wrap your AddSubscriber page with withRouter HOC
class AddSubscriber extends React.Component{
...some-code....
}
export default withRouter(AddSubscriber)
you need navigation logic (this.props.history.push("/"))
to be located a addScubscribers page.
(you have it at "homepage" instead)
I am passing data from my child component (Header component) to my parent component (App component). My idea: when I click on my creating button in header component I want to send information to app component to hide header and footer. Here is code example.
Header.jsx:
import React, { Component } from 'react';
import { Nav, Navbar, Button } from "react-bootstrap";
class Header extends Component {
constructor(props) {
super();
this.state = {
showHeaderAndFooter: false
};
}
onChangeChildComponent = () => {
this.props.changeVisibilityOfHeaderAndFooter(this.state.showHeaderAndFooter);
}
render() {
return (
<Navbar bg="dark" variant="dark">
<Nav className="mr-auto">
<Button href="/createGym" onClick={this.onChangeChildComponent.bind(this)}> Create </Button>
</Nav>
</Navbar>
);
}
}
export default Header;
App.js:
import React, { Component, Fragment } from 'react';
import './App.css';
import './components/header';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Header from './components/header';
import Footer from './components/footer';
import Home from './components/home';
import CreateGym from './components/create-gym';
import Login from './components/authentication/login';
class App extends Component {
constructor() {
super();
this.state = {
showHeaderAndFooter: true
};
}
onChangeChildComponent (showHeaderAndFooter) {
this.setState(
{
showHeaderAndFooter: showHeaderAndFooter
});
}
Container = () => (
<div>
{ this.state.showHeaderAndFooter &&
<Header
changeVisibilityOfHeaderAndFooter = {this.onChangeChildComponent.bind(this)}
/>
}
<div className="container">
<Route exact path='/' component={Home} />
<Route path="/createGym" component={CreateGym} />
<Route path="/login" component={Login} />
</div>
{ this.state.showHeaderAndFooter && <Footer /> }
</div>
)
render() {
console.log(this.state.showHeaderAndFooter);
return (
<BrowserRouter>
<Switch>
<Fragment>
{ <Route component={this.Container}/> }
</Fragment>
</Switch>
</BrowserRouter>
);
}
}
export default App;
The problem is because my code is entering App constructor twice. At the first time, everything is fine, but on the second time, this boolean showHeaderAndFooter is again set to true because that is the default value. Any idea how to solve this?
you shouldn't be passing from child > parent. react is uni-directional (that means data can only flow in one way, and that way is downwards)
to achieve this, move the state into the parent
class Parent extends React = {
this.state = { showHeaderAndFooter: false }
functionToToggleShowing = () => {
this.setState({showHeaderAndFooter: !this.state.showHeaderAndFooter})
}
render() {
return(
<Child showHeaderAndFooter={this.state.showHeaderAndFooter} functionToToggleShowing={functionToToggleShowing} />
)
}
}
that is pseduo code but essentially move the state to the parent and pass down the state to the child as well as a way to change that state
React allows passing of control from child to parent by means of props.
Here is the 3-step procedure to make it work:
Step 1: Create a data member in Parent, which manipulates parent state.
Step 2: Send the parent's data member as a prop to the child.
Step 3: In the child, Call the prop sent by parent for responding to an event.
Here is a demonstration of passing control from child to parent, using the above technique:
The example has 2 components -
Parent component is App.jsx
Child component is Header.jsx
Parent has a state-manipulating data member called 'hideHeader'.
Parent passes hideHeader as a prop called onClick to the child.
Child calls the prop sent by parent, in response to its onClick event.
Parent component - App.jsx
import React, { Component } from 'react'
import Header from "./Header";
export default class App extends Component {
constructor() {
super();
this.state = {
showHeader: true
}
}
{/* A state-manipulating data member */}
hideHeader = () => {
this.setState({showHeader: false})
}
render() {
return (
<div>
{/* State-manipulating data member sent as prop to child */}
{ this.state.showHeader &&
<Header onClick={this.hideHeader} />
}
</div>
)
}
}
Child component - Header.jsx
import React, { Component } from "react"
export default class Header extends Component {
render() {
return (
<div>
<h1>Header Demo</h1>
{/* Call the prop sent by parent when button is clicked */}
<button onClick={this.props.onClick}>Hide</button>
</div>
)
}
}
Output:
Before 'Hide' button is clicked:
After 'Hide' button is clicked:
I'm using a template for my React app, and I came across one component that constantly gives me the following warning:
Warning: componentWillMount has been renamed, and is not recommended for use. See https:... for details.
Now, I'm trying to rewrite this component to React Hooks, to avoid multiple warnings in the console.
This is how the component looks like:
import React, { Component } from 'react';
import Nprogress from 'nprogress';
import ReactPlaceholder from 'react-placeholder';
import 'nprogress/nprogress.css';
import 'react-placeholder/lib/reactPlaceholder.css';
import CircularProgress from '../components/CircularProgress/index';
export default function asyncComponent(importComponent) {
class AsyncFunc extends Component {
constructor(props) {
super(props);
this.state = {
component: null,
};
}
componentWillMount() {
Nprogress.start();
}
componentWillUnmount() {
this.mounted = false;
}
async componentDidMount() {
this.mounted = true;
const { default: Component } = await importComponent();
Nprogress.done();
if (this.mounted) {
this.setState({
component: <Component {...this.props} />,
});
}
}
render() {
const Component = this.state.component
|| (
<div
className="loader-view"
style={{ height: 'calc(100vh - 200px)' }}
>
<CircularProgress />
</div>
);
return (
<ReactPlaceholder type="text" rows={7} ready={Component !== null}>
{Component}
</ReactPlaceholder>
);
}
}
return AsyncFunc;
}
And here is the example of its usage:
import React from 'react';
import { Route, Switch, withRouter } from 'react-router-dom';
import asyncComponent from '../util/asyncComponent';
const Routes = ({ match }) => (
<Switch>
<Route
path={`${match.url}/main`}
component={asyncComponent(() => import('./routes/MainPage/index'))}
/>
{/* <Route component={asyncComponent(() => import("app/routes/extraPages/routes/404"))}/> */}
</Switch>
);
export default withRouter(Routes);
I know how to rewrite component lifecycle methods (componentDidMount, componentWillUnmount should be rewritten to useEffect), but I don't understand part with props - asyncComponent gets importComponent as a prop, but where are we getting props in AsyncFunc? And how this could be rewritten to the functional component?
As I understood, asyncComponent is a Higher Order Component that responds with an updated component.
Sorry for not providing a sandbox for this example.
I couldn't test this code but I think is a solution
import React, { useState, useEffect } from 'react';
import Nprogress from 'nprogress';
import ReactPlaceholder from 'react-placeholder';
import 'nprogress/nprogress.css';
import 'react-placeholder/lib/reactPlaceholder.css';
import CircularProgress from '../components/CircularProgress/index';
const asyncComponent = importComponent => {
const [component, setComponent] = useState(null);
Nprogress.start();
useEffect(async () => {
const { default: Component } = await importComponent();
Nprogress.done();
setComponent(<Component {...importComponent} />);
}, []);
return component ? (
<ReactPlaceholder type="text" rows={7} ready>
{component}
</ReactPlaceholder>
) : (
<div className="loader-view" style={{ height: 'calc(100vh - 200px)' }}>
<CircularProgress />
</div>
);
};
export default asyncComponent;
I don't see the need to use the state mounted because you only use it in the dismount to setState component, but if 2 lines before you set mounted as true, it is not necessary to generate a re-render, you can go and setState component directly.
I hope this helps you.
According to reactjs.org, componentWillMount will not be supported in the future. https://reactjs.org/docs/react-component.html#unsafe_componentwillmount
There is no need to use componentWillMount.
Before discussing your question but where are we getting props in AsyncFunc?
start with an example
const A = props => {
return <p>{JSON.stringify(props.location)}</p>;
};
function App() {
return (
<Switch>
<Route exact path="/" component={A} />
<Route exact path="/a" component={p => <A {...p} />} />
</Switch>
);
}
Route / and route /a both components are getting props but in different ways
on route / the props are automatically passed to the component
As you know HOC take a component and in response, it returns a component
so asyncComponent(() => import('./routes/MainPage/index') will return AsyncFunc
so we can simply say that
<Route
path={`${match.url}/main`}
component={AsyncFunc}
/>
and that's all
component={AsyncFunc} is equal to component={(p) => <AsyncFunc {...p} />}
and that how pros are passing
I have a problem with passing context to route. I get an error when i click a link that goes to my component where context was passed from App component. Below is that component with App (only one import just to show where Context is coming from):
App.js
import { Context } from './Context';
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
cryptolist: []
}
}
componentDidMount = () => {
fetch('https://api.coinmarketcap.com/v2/ticker/?structure=array')
.then(response => response.json())
.then(json => this.setState({
cryptolist: json.data
}))
}
render() {
return (
<div>
<Menu />
<Context.Provider value={this.state}>
<Userlist />
</Context.Provider>
</div>
)
}
}
export default App;
Userlist.js ( should be cryptolist or something )
import { Context } from '.././Context'
export default class Userlist extends Component {
render() {
return (
<main>
<Context.Consumer>
{(context) => context.cryptolist.map(el => {
return (
<div>
<h2>{el.name}</h2>
<h5>{el.symbol}</h5>
<h3>{el.quotes.USD.price}</h3>
</div>
)
})}
</Context.Consumer>
</main>
)
}
}
Context.js
import React from 'react';
export const Context = React.createContext();
Everything works just fine here untill i wanted to make a menu that links to this component.
import React from "react";
import { slide as Slider } from 'react-burger-menu';
import { BrowserRouter as Router, Route, Link, Switch} from "react-router-dom";
import Main from './main';
import Userlist from './userlist';
export default class Menu extends React.Component {
render () {
return (
<Router>
<div className="bg-navy w-100 h-100">
<Slider width={ 180 } isOpen={ false }>
<Link className="menu-item" to="/main">Home</Link>
<Link className="menu-item" to="/crypto">About</Link>
</Slider>
<Switch>
<Route path="/main" component={Main} />
<Route path="/crypto" component={Userlist} />
</Switch>
</div>
</Router>
);
}
}
When i click a link to component Userlist i get an error thats cryptolist is not defined. I get it that Userlist can't see a context after clicking link to it. How to pass it correctly?
You are using the routes in the Menu component. Is this really you want? Though, I don't know how this slide thingy works. Maybe this is the way you want to go. I think your problem occurs because your Menu component is not wrapped by the provider. Try like this:
<Context.Provider value={this.state}>
<Menu />
<Userlist />
</Context.Provider
Your Menu component will call Userlist but as it is out the Provider the context doesn’t exist!
Replace Userlist in Context.Provider by Menu and all will be fine.
I am sorry for my stupid question but i am really new in react and this problem make me stuck for days. I am kinda confused to make a login page in reactjs. my app.js code is like this :
import React from 'react';
import {HashRouter as Router, Route} from 'react-router-dom';
import asyncComponent from './AsyncComponent';
import AppShell from './AppShell';
import Login from './login/Login';
const Dashboard = asyncComponent(() => {
return import(/* webpackChunkName: "dashboard" */ './dashboard/Dashboard')
.then(module => module.default);
});
const LoginPage = asyncComponent(() => {
return import(/* webpackChunkName: "login" */ './login/Login')
.then(module => module.default);
});
class App extends React.Component {
render() {
return (
<Router>
<AppShell>
<div>
<Route exact path="/" component={Dashboard} />
<Route path="/login" component={LoginPage} />
</div>
</AppShell>
</Router>
);
}
}
export default App;
And this is my AppShell code :
import React, {Component} from 'react';
import {Link} from 'react-router-dom';
import {MuiThemeProvider} from 'material-ui/styles';
import {AppBar, Drawer, MenuItem} from 'material-ui';
import {DashboardIcon} from './icon/Icons';
import ArrowDropRight from 'material-ui/svg-icons/navigation-arrow-drop-right';
const ContentStyle = {
width: '90%',
margin: 'auto',
marginTop: '30px'
};
class SidebarDrawer extends React.Component {
componentDidMount() {
let frameCount = 0;
const open = () => (frameCount++ > 0) ? this.props.onMounted() :
requestAnimationFrame(open);
requestAnimationFrame(open);
}
render() {
return (
<Drawer
docked={false}
width={200}
open={this.props.open}
onRequestChange={this.props.onRequestChange}
>
<MenuItem
primaryText={'Dashboard'}
leftIcon={<DashboardIcon/>}
containerElement={<Link to={'/'}/>}
onClick={this.props.onClick}
/>
</Drawer>
);
}
}
class AppShell extends Component {
constructor(props) {
super(props);
this.state = {
open: false,
drawer : false
};
}
handleDrawerToggle = (e) => {
if (!this.state.drawer) {
this.setState({drawer: true});
e.preventDefault();
} else {
this.setState({open: !this.state.open});
}
}
render() {
const LazySidebarDrawer = this.state.drawer && (<SidebarDrawer
open={this.state.open}
onMounted={() => this.setState({open: true})}
onClick={() => this.setState({open: false})}
onRequestChange={open => this.setState({open: open})}
/>)
return (
<MuiThemeProvider>
<div>
<AppBar
title="Dashboard"
iconClassNameRight="muidocs-icon-navigation-expand-more"
onLeftIconButtonTouchTap={this.handleDrawerToggle}
/>
{LazySidebarDrawer}
<div id="content" style={ContentStyle}>
{React.cloneElement(this.props.children)}
</div>
</div>
</MuiThemeProvider>
);
}
};
export default AppShell;
But i still can access dashboard when i open login page. How is the correct pattern for login page?
Thanks
Your routing is correct, the exact '/' will only render the Dashboard component when the path is '/'. What you're seeing is the dashboard drawer or AppBar component. The dashboard drawer is still there in the login screen because it's always there in the AppShell code and your routes are children of AppShell. A potential solution would be to move that AppBar component to your Dashboard component if you only want it there.