I am trying to do a single page application navigation, so the components load into the page container when the user clicks on menu links. This is what I've tried:
App.tsx:
import * as React from 'react';
import Home from './components/Home';
import MainMenu from './components/MainMenu';
import SignUp from './components/SignUp';
interface IProps {
SignUp?: boolean;
}
interface IState{
SignUp: boolean;
}
class App extends React.Component<IProps,IState>
{
constructor(props: IProps, state: IState) {
super(props);
this.renderPage = this.renderPage.bind(this);
this.state = state;
this.setState({
SignUp: false
});
}
public renderPage(page: string)
{
if(page === 'SignUp')
{
this.setState({
SignUp: true
});
}
}
public render()
{
let page = <Home/>;
if(this.state.SignUp)
{
page = <SignUp/>;
}
return (
<div className="App">
<MainMenu renderPage={this.renderPage}/>
<div className="container-fluid">
{page}
</div>
</div>
);
}
}
export default App;
MainMenu.tsx:
import * as React from 'react';
import './Home.css';
interface IProps
{
renderPage: (page: string) => void
}
class MainMenu extends React.Component<IProps> {
public render() {
return (
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav mr-auto">
<li className="nav-item active">
<a className="nav-link" href="#" onClick={this.props.renderPage('Home')}>
Home
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#" onClick={this.props.renderPage('SignUp')}>
Sign Up
</a>
</li>
</ul>
</div>
</nav>
);
}
}
export default MainMenu;
I got this error in MainMenu.tsx:
Type 'void' is not assignable to type '((event:
MouseEvent) => void) | undefined'.
Inside MainMenu component you invoke the passed function, not assingn it to an a element.
<a className="nav-link" href="#" onClick={() => this.props.renderPage('SignUp')}>
Sign Up
</a>
The example above should solve the error, even though you don't pass the event argument.
However, wouldn't it be better if you used react-router, which handles rendering components depending on navigation?
Related
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 want to for example only show the logout button if a user is actually logged in.
My _app.js file:
import React from "react";
import App from "next/app";
import Navbar from "../components/navbar";
import Layout from "../components/layouts/mainLayout";
import { handleAuthSSR } from "../utils/auth";
export default class MyApp extends App {
render() {
const { Component, pageProps, userAuth } = this.props;
return (
<Layout userAuth={pageProps.token}>
<Component {...pageProps} />
</Layout>
);
}
}
Main Layout:
import Head from "next/head";
import Navbar from "../navbar";
const Layout = props => {
return (
<>
<Head>
<title>My App</title>
<link
rel="stylesheet"
href="https://bootswatch.com/4/cerulean/bootstrap.min.css"
></link>
<link href="/static/css/styles.css" rel="stylesheet"></link>
</Head>
<Navbar />
<div className="container">{props.children}</div>
</>
);
};
export default Layout;
Navbar:
import Link from "next/link";
import { logout } from "../utils/auth";
const Navbar = props => (
<nav className="navbar navbar-expand navbar-dark bg-dark mb-4">
<div className="container">
<a className="navbar-brand" href="#">
My App
</a>
<div className="collapse navbar-collapse">
<ul className="navbar-nav ml-auto">
<li className="nav-item">
<Link href="/">
<a className="nav-link">Home</a>
</Link>
</li>
<li className="nav-item">
<Link href="/about">
<a className="nav-link">About</a>
</Link>
</li>
<li className="nav-item">
<Link href="/register">
<a className="nav-link">Register</a>
</Link>
</li>
{props.userAuth ? (
<li>
<button className="btn btn-primary" onClick={logout}>
Logout
</button>
</li>
) : (
<li className="nav-item">
<Link href="/login">
<a className="nav-link">Login</a>
</Link>
</li>
)}
</ul>
</div>
</div>
</nav>
);
export default Navbar;
So, I am passing userAuth via props but I haven't actually got the logic for userAuth.
The handleAuthSSR looks like:
export const handleAuthSSR = ctx => {
const { token } = nextCookie(ctx);
if (ctx.req && !token) {
ctx.res.writeHead(302, { Location: "/login" });
ctx.res.end();
return;
}
if (!token) {
console.log("no token found");
Router.push("/login");
}
return token;
};
I am using this as part of a HOC when a user navigates around and it works but not sure how to get this to work with regards to the navigation show/hide.
I can see that you didn't pass the userAuth prop to the navbar component in mainLayout.js. Also you need to work on your Parent/Children props communications.
Maybe you can use your HOC to return token or false (or null instead of false). Use it then in navbar.js to toggle Login/Logout link.
so I have been trying to create dynamic menu in .net core + react environment
the problem I have been facing is basically this error message
Type '{}' is not assignable to type
'Readonly<RouteComponentProps<{}>>'.
Property 'match' is missing in type '{}'
Here is the code for
Layout.tsx
import * as React from 'react';
import { NavMenu } from './NavMenu';
export interface LayoutProps {
children?: React.ReactNode;
}
export class Layout extends React.Component<LayoutProps, NavMenu> {
public render() {
return <div className='container-fluid'>
<div className='row'>
<div className='col-sm-3'>
<NavMenu />
</div>
<div className='col-sm-9'>
{ this.props.children }
</div>
</div>
</div>;
}
}
the other file is NavMenu.tsx
import * as React from 'react';
import { Link, NavLink } from 'react-router-dom';
import { RouteComponentProps } from 'react-router';
import * as ReactDOM from 'react-dom';
interface navMenuItems {
menuItemsList: NavMenuPages[];
loading: boolean;
}
export class NavMenu extends React.Component<RouteComponentProps<{}>, navMenuItems> {
constructor() {
super();
this.state = { menuItemsList: [], loading: true };
fetch('api/Menu')
.then(response => response.json() as Promise<NavMenuPages[]>)
.then(data => {
this.setState({ menuItemsList: data, loading: false });
});
}
public render() {
this.renderMenu(this.state.menuItemsList);
return <div></div>;
}
public renderMenu(menuItemsList: NavMenuPages[]) {
return <div className='main-nav'>
<div className='navbar navbar-inverse'>
<div className='navbar-header'>
<button type='button' className='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
<span className='sr-only'>Toggle navigation</span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
</button>
<Link className='navbar-brand' to={'/'}>ReactCrudDemo</Link>
</div>
<div className='clearfix'></div>
<div className='navbar-collapse collapse'>
<ul className='nav navbar-nav'>
{menuItemsList.map(mil =>
<li>
<NavLink to={`${mil.toLink}`} exact activeClassName='active'>
<span className='glyphicon glyphicon-home'></span> {mil.name}
</NavLink>
</li>
)}
</ul>
</div>
</div>
</div>;
}
}
export class NavMenuPages{
name: string = "";
toLink: string = "";
isShown: boolean = true;
}
remaining files are the same as this tutorial:
https://www.c-sharpcorner.com/article/asp-net-core-crud-with-reactjs-and-entity-framework-core/
You are declaring NavMenu as requiring the RouteComponentProps<{}>, which it would normally receive automatically if you call it via <Route>, but you are calling it directly without passing the required props. Since NavMenu does not use any of the RouteComponentProps, you can just change the props type. Replace:
export class NavMenu extends React.Component<RouteComponentProps<{}>, navMenuItems>
with:
export class NavMenu extends React.Component<{}, navMenuItems>
I need to filter AssessmentCards by Year. I made the method.
But I need to call clickAllCards and clickYearCard method in onClick event on other file. How can I do that?
This is my code with the methods, I'm using Pug.JS to render:
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import messages from './messages';
import { getAssessmentsCards } from '../../functions';
import template from './index.pug';
const cardsAssessments = getAssessmentsCards();
export default class CardAssessment extends React.PureComponent { // eslint-disable-line react/prefer-stateless-function
constructor(props){
super(props);
this.state = {
listCards: [],
openCm: false,
}
}
componentWillMount(){
this.setState({listCards: cardsAssessments});
}
hover() {
this.setState({openCm: !this.state.openCm});
}
clickAllCards(e){
e.preventDefault();
this.setState({listCards: cardsAssessments});
}
clickYearCard(e){
e.preventDefault();
var filtered = cardsAssessments.filter((data) => {
return data.yearCard === '2018';
});
this.setState({listCards: filtered});
}
render() {
let cm = ["card-menu"];
if(this.state.openCm) {
cm.push('active');
}
return template.call(this, {
messages,
FormattedMessage,
Link,
cm
});
}
}
This is my pug file:
.card-adjust
div(href="" onClick="{this.clickYearCard.bind(this)}") 2018
div(href="" onClick="{this.clickAllCards.bind(this)}") All
Link.card.add-new(to="/add-assessment")
span
.add-icon
i.ti-plus
|
FormattedMessage(__jsx='{...messages.addAssessment}')
.card.card-materia(#for='data in this.state.listCards', key='{data.id}')
.card-body(id="{data.id}")
div(className="{cm.join(' ')}" onClick="{this.hover.bind(this)}")
i.fas.fa-ellipsis-v
.cm-floating
Link.cmf-agenda(to="/agendamento")
i.ti-agenda
|
FormattedMessage(__jsx='{...messages.scheduled}')
Link.cmf-copy(to="#")
i.pe-7s-copy-file
|
FormattedMessage(__jsx='{...messages.copy}')
Link.cmf-trash(to="#")
i.ti-trash
|
FormattedMessage(__jsx='{...messages.delete}')
.cm-icon
i(className='{data.icon}')
h2.cm-title {data.disciplineAbbreviation}
span.badge.badge-danger {data.status}
p.cm-questions {data.questionNumber}
FormattedMessage(__jsx='{...messages.questions}')
.cm-info
Link(to="#") {data.disciplineName}
Link(to="#") {data.year}
Link(to="#") {data.segment}
.cm-date
//- i.pe-7s-refresh-2
| {data.date}
And this is the file where I need to put the onClick event:
import React from 'react';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
import template from './index.pug';
import '../../assets/scss/main.scss';
export default function (params = {}) {
const { messages, FormattedMessage } = params;
return (
<div>
<ul className="nav nav-tabs">
<li className="nav-item">
<a className="nav-link" href="#">
<FormattedMessage {...messages.all} />
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">2018</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">2017</a>
</li>
</ul>
<div className="navigation-tabs display-none">
<a>
<i className="nt-icon ti-angle-left" />
</a>
1 de 3
<a>
<i className="nt-icon ti-angle-right" />
</a>
</div>
</div>
);
}
Thanks
You can pass any method for onClick event to any component like that:
App.js
class App extends React.Component {
handleClick = () => alert( "Clicked" );
render() {
return (
<div>
<Child click={this.handleClick}/>
</div>
)
}
}
or with a function component if you don't need lifecylce methods or "this" (here we don't need):
const App = () => {
const handleClick = () => alert( "Clicked" );
return (
<div>
<Child click={handleClick}/>
</div>
);
}
Child.js
const Child = ( props ) => (
<div>
<button onClick={props.click}>Click me!</button>
</div>
)
I have implemented pagination component which has 5 pages in total. Pagination is child component of home component.
When I click on page 1 it should get page number through getpagenumber() and handleClick() which makes use of page number but in below code getpagenumber() does not work when we click on page number.
pagination.js:
import React, { Component } from 'react';
class Pagination extends Component {
getpagenumber(val){
return val;
}
handleClick(){
this.setState({
end:getpagenumber()*16,
start:end-16
});
const end = getpagenumber()*16;
this.props.onChange(end - 16, end);
}
render() {
return (
<div>
<Content/>
<div className="container">
<ul className="pagination">
<li {this.getpagenumber(1)} onClick={this.handleClick}>1</li>
<li {this.getpagenumber(2)} onClick={this.handleClick}>2</li>
<li {this.getpagenumber(3)} onClick={this.handleClick}>3</li>
<li {this.getpagenumber(4)} onClick={this.handleClick}>4</li>
<li {this.getpagenumber(5)} onClick={this.handleClick}>5</li>
</ul>
</div>
</div>
);
}
}
export default Pagination;
home.js :
import React, { Component } from 'react';
import Pagination from './pagination';
import Content from './content';
class Home extends Component {
constructor(props){
super(props);
this.state = {
start:0,
end:16
};
}
onChangePagination = (start, end) => {
this.setState({
start:start,
end:end
});
};
render() {
return (
<div>
<Content start={start} end={end}/>
<Pagination onChange={this.onChangePagination}/>
</div>
);
}
}
export default Home;
You are exaggerating a bit. All you need is this:
import React, { Component } from 'react';
class Pagination extends Component {
handleClick(value){
this.setState({
end: value*16,
start: end-16
});
const end = value*16;
this.props.onChange(end - 16, end);
}
render() {
return (
<div>
<Content/>
<div className="container">
<ul className="pagination">
<li><a href="#" onClick={this.handleClick.bind(this, 1)}>1</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 2)}>2</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 3)}>3</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 4)}>4</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 5)}>5</a></li>
</ul>
</div>
</div>
);
}
}
export default Pagination;
Notes:
Maybe consider even passing the number of pages to the Pagination component and then generating an array from 1 to that number and just mapping out the li with the clicks.
Clicks should go on the link tags.