I would like to set up a portfolio page in react which would have different pages (CV, News, Works), and they would be different components.
What bothers is me though that setting up like this, I cannot do an "App.js" as "mama-component" because all the components are in different url and they have at least one different component inside them. For example my Curriculum component:
import React from 'react';
import Header from './Header';
class Curriculum extends React.Component {
render() {
return (
<React.Fragment>
<Header />
<article className="curriculum">
<div className="curriculum-text">
<ul>
<li>
2022
<ul>
<li>Participating in ..........</li>
<li>Released my co-authored article "Three Ways of .....</li>
</ul>
</li>
<li>
2021
<ul>
<li>
Starting to work at the ..........
<ul>
<li>Working on the ......</li>
<li>Assisting to ......</li>
<li>Reviewing and drafting contracts</li>
</ul>
</li>
<li>Participating IRIS conference with my paper titled "...." ......</li>
</ul>
</li>
<li>
2020
<li></li>
</li>
</ul>
</div>
</article>
</React.Fragment>
);
}
}
export default Curriculum;
My News component:
import React, { Fragment } from 'react';
import Header from './Header';
class News extends React.Component {
goToNews = (event) => {
event.preventDefault();
const currentNews = event.target.parentElement.id;
this.props.history.push(`/news/${currentNews}`);
console.log(this.props);
};
render() {
return (
<React.Fragment>
<Header />
<article className="news">
<div className="news-text">
<h2>News from the World </h2>
<div id="news1" value="news1" onClick={this.goToNews}>
<h3>News1</h3>
</div>
<div id="news2" value="news2" onClick={this.goToNews}>
<h3>News2</h3>
</div>
<div id="news3" value="news3" onClick={this.goToNews}>
<h3>News3</h3>
</div>
</div>
</article>
</React.Fragment>
);
}
}
export default News;
I tried to convert my Router.js to an App component as this:
import React from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Introduction from './Introduction';
import Curriculum from './Curriculum';
import Works from './Works';
import News from './News';
import CurrentNews from './CurrentNews';
import NotFound from './NotFound';
class App extends React.Component {
yellAtMe = (fish) => {
console.log('heeeeeeeeeeeeeeeeeee');
};
render() {
return (
<Router yellAtMe={this.yellAtMe}>
<Switch yellAtMe={this.yellAtMe}>
<Route exact path="/" component={Introduction} />
<Route exact path="/curriculum" component={Curriculum} />
<Route yellAtMe={this.yellAtMe} exact path="/works" component={Works} />
<Route exact path="/news" component={News} />
<Route path="/news/:newsId" component={CurrentNews} />
<Route component={NotFound} />
</Switch>
</Router>
);
}
}
export default App;
However I cannot pass through Route, Switch and Router props to the components (at least I did not succeed).
My questions are:
am i allowed to set up more states for each component? (that would contradict to Wes Boss's advice according which the state should be a "single source of truth")
is there any way to pass props through the Router components? (or do you think my solution to convert it to an App.js is adequate)
Should I somehow put all my components in one big component? (so somehow it would result like that:) - but then how to make them each as a different page with different url?)
<App />
<Header />
<Always-change-component />
<CV />
<News />
<Works />
Thanks in advance!
"am i allowed to set up more states for each component? (that would contradict to Wes Boss's advice according which the state should be a "single source of truth")" - you can create states for each component, but in that state you should keep only internal information that is related and used in this component and all common information you should pass through props from parent component.
"is there any way to pass props through the Router components? (or do you think my solution to convert it to an App.js is adequate)" - you should keep shared information in parent component, just rewrite Routes structure like this:
<Router>
<Switch>
<Route exact path="/">
<Introduction yellAtMe={this.yellAtMe}/>
<Route />
....
</Switch>
</Router>
Should I somehow put all my components in one big component? (so somehow it would result like that:) - but then how to make them each as a different page with different url?) - answer is above.
Related
I am new to react and react-router, so please go easy on me.
I am trying to implement router in my Todo List project, where path="/" takes me to my todo list and path="/id" takes me to a test page (later will show the description of the task).
When I click the link that takes me to "/id", the URL in the browser changes but the page/content doesn't. However, when I refresh my browser, the test page loads.
I have put the Switch in App.js shown below.
import React, { Component } from "react";
import "./App.css";
import TodoList from "./components/TodoList";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Test from "./components/Test";
class App extends Component {
render() {
return (
<Router>
<div className="todo-app">
<p>
<Link to="/">Home</Link>
</p>
<Switch>
<Route exact path="/" component={TodoList} />
<Route path={`/id`} component={Test} />
</Switch>
</div>
</Router>
);
}
}
export default App;
And I have put the Link to "/id" as shown below in a child component of component which is called here in App.js.
<div key={todo.id}>
<Link className="todo-text" to={`/id/${todo.id}`}>
{todo.text}
</Link>
</div>
Am I missing something which is causing my component to not load when I click the link?
Edit: Here's a link to my project. https://stackblitz.com/edit/react-7cpjp9?file=src/index.js
Issue
Ok, the issue is exactly as I had suspected. You are rendering multiple routers in your app. The first is a BrowserRouter in your index.js file, the second, another BrowserRouter in App.js, and at least a third BrowserRouter in Todo.js. You need only one router to provide a routing context for the entire app.
The issue here is that the router in Todo component is the closest router context to the links to specific todo details. When a link in Todo is clicked, this closest router handles the navigation request and updates the URL in the address bar. The blocks, or "masks", the router in App component or index.js that is rendering the routes from "seeing" that a navigation action occurred. In other words, the URL in the address bar is updated by the inner router, but the outer router doesn't know to render a different route.
Solution
Keep the BrowserRouter wrapping App in index.js and remove all other routers used in your app.
App - Remove the Router component. Also, reorder the routes/paths from most specific to least specific so you don't need to specify the exact prop on every route. Allows more specific paths to be matched and rendered before less specific paths by the Switch component.
class App extends Component {
render() {
return (
<div className="todo-app">
<p>
<Link to="/">Home</Link>
</p>
<Switch>
<Route path="/id/:todoId" component={Test} />
<Route path="/" component={TodoList} />
</Switch>
</div>
);
}
}
Todo - Remove the Router component. Move the key={todo.id} up to the outer-most element so when todos array is updated React can reconcile updates.
class Todo extends Component {
constructor(props) {
super(props);
this.state = {
id: null,
value: "",
details: "",
};
this.submitUpdate = this.submitUpdate.bind(this);
}
submitUpdate(value) {
const { updateTodo } = this.props;
updateTodo(this.state.id, value);
this.setState({
id: null,
value: "",
});
}
render() {
const { todos, completeTodo, removeTodo } = this.props;
if (this.state.id) {
return <TodoForm edit={this.state} onSubmit={this.submitUpdate} />;
}
return todos.map((todo, index) => (
<div
className={todo.isComplete ? "todo-row complete" : "todo-row"}
key={todo.id}
>
<div>
<Link className="todo-text" to={`/id/${todo.id}`}>
{todo.text}
</Link>
</div>
<div className="icons">
<RiCloseCircleLine
onClick={() => removeTodo(todo.id)}
className="delete-icon"
/>
<TiEdit
onClick={() => this.setState({ id: todo.id, value: todo.text })}
className="edit-icon"
/>
<RiCheckboxCircleLine
onClick={() => completeTodo(todo.id)}
className="delete-icon"
/>
</div>
</div>
));
}
}
First of all the approach, you are taking for dynamic routing is wrong.
It should be like this you will have to add the exact keyword on the dynamic route.
<Route exact path="/id/:todoId" component={Test} />
And
<div key={todo.id}>
<Link className="todo-text" to={`/id/${todo.id}`}>
{todo.text}
</Link>
import React, { Component } from "react";
import "./App.css";
import TodoList from "./components/TodoList";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Test from "./components/Test";
class App extends Component {
render() {
return (
<Router>
<div className="todo-app">
<p>
<Link to="/">Home</Link>
</p>
<Switch>
<Route exact path="/" component={TodoList} />
**<Route exact path={`/id`} component={Test} />**
</Switch>
</div>
</Router>
);
}
}
export default App;
new to React Router, my question is how to render a particular component inside other layout which is already rendered (i have two components sidebar and content i just want if i click on any link in sidebar that component will we render in already render Content component not override that)
////////////Sidebar.js////////////
import React from 'react'
import { BrowserRouter, Link } from 'react-router-dom'
import PersonalImg from '../images/personal.gif'
const Sidebar = () => {
return (
<div className="sidebar">
<BrowserRouter>
<div className="personal-img">
<img src={PersonalImg} alt="personl-img" />
</div>
<div className="navigation">
<ul className="list">
<li><Link to="/about">About</Link></li>
<li><Link to="/work">Work</Link></li>
<li><Link to="/skills">Skills</Link></li>
<li><Link to="/contact">Contact</Link></li>
</ul>
</div>
</BrowserRouter>
</div>
)
}
export default Sidebar;
Content component...
/////////////////Content.js//////////////////
import React from 'react'
import { BrowserRouter, Route } from 'react-router-dom'
import About from './About'
import Skills from './Skills'
import Work from './Work'
import Contact from './Contact'
const Content = (props) => {
return (
<div className="content">
<BrowserRouter>
<Route path="/" componet={About} />
<Route path="/work" componet={Work} />
<Route path="/contact" componet={Contact} />
<Route path="/skills" componet={Skills} />
</BrowserRouter>
</div>
)
}
export default Content;
and thats how App.js rendering these components
render() {
return (
<Fragment>
<Sidebar />
<Content />
</Fragment>
)
}
Here, I have created small demo for you
https://codesandbox.io/s/naughty-glade-vnj0l
Problem 1: componet spell is wrong. It should be component in the Route.
Problem 2: set exact keyword for the first route like this <Route exact path="/" componet={About} />.
Use single BrowserRouter throughout the application.
i have routing attribure in my react.js app. when i click on the route for the first time it works fine but if i refresh in the page it gives error saying cannot read property ... of null. I'm making api call from tmdb. Why i get error on refresh? Secondly, i have another route which i cannot access to it unless it is above the first route i mention. I mean when i put TvInfo above the MovieInfo tvinfo works movie info doesn't. This way only movieinfo works. If i click the above route elements first then clicking tvinfo element i get the previous clicked movie on the screen again. What could be the problem i tried many things? thanks
import React,{ Component } from 'react';
import Home from './components/Home';
import MovieInfo from './components/MovieInfo';
import TvInfo from './components/TvInfo';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
class App extends Component {
render(){
return(
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/:movie_id" component={MovieInfo} />
<Route path="/:tv_id" component={TvInfo} />
</Switch>
</BrowserRouter>
)
}
}
export default App;
You need to design your Routes a little differently, Route component cannot differentiate between two separate params.
class App extends Component {
render(){
return(
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />
<Route path="movie/:movie_id" component={MovieInfo} />
<Route path="tv/:tv_id" component={TvInfo} />
</Switch>
</BrowserRouter>
)
}
}
export default App;
this is my movieInfo component
class MovieInfo extends React.Component{
render(){
return(
<div>
<p style={{marginLeft:'47%',fontSize:25}}>{this.props.selectedMovie.original_title}</p>
<img style={{marginLeft: '20%'}}
src={`http://image.tmdb.org/t/p/w780${this.props.selectedMovie.backdrop_path}`} alt="moviebackdrop"/>
<div style={{float:'right',marginRight:45}}>
<p>Release Date: {this.props.selectedMovie.release_date}</p>
<p>Vote: {this.props.selectedMovie.vote_average}<Icon name="star" color="yellow" /></p>
</div>
<p style={{width:800,marginLeft: '20%'}} >{this.props.selectedMovie.overview}</p>
<p>{this.props.data.homepage}</p>
</div>
)}
}
const mapStateToProps = (state) => {
return{
selectedMovie:state.movie.selectedMovie,
}
}
to be able to display this screen i have another component where you show the images for the movies. when i click on the image it renders movieInfo component.
here is my image component.
selectMovie = () => {
this.props.setMovie(this.props.movie)
}
render(){
return(
<Link to={"movie/" + this.props.movie.id}>
<div onClick={() => this.selectMovie()}>
<div className="item">
<img className="img"
src={`http://image.tmdb.org/t/p/w342${this.props.movie.backdrop_path}`} alt="moviebackdrop"/>
<p className="title">{this.props.movie.original_title}</p>
<p className="overview">{this.props.movie.overview}</p>
</div>
</div>
</Link>
)}
}
i'm trying to use react router in my reactjs app. And I encountered this problem:
This is what i'm trying to do:
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import About from '../Pages/About';
import Home from '../Pages/Home';
import Topics from '../Pages/Topics';
import LinkTest from '../Pages/LinkTest';
class AppRouter extends Component {
render() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/home">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/topics">Topics</Link>
</li>
<Route path="/home" component={LinkTest}/>
</ul>
<hr />
<Route path="/home" component={Home}/>
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
</Router>
);
}
}
export default AppRouter;
Ignore "about" and "topic" component, when I click on "Home" link, it should target 2 routes, one will render "LinkTest" and the other renders "Home".
This is what inside "LinkTest":
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class LinkTest extends Component {
render() {
const {match}=this.props;
return (
<div>
<Link to={`${match.url}/Test`}>Link To Test</Link>
</div>
);
}
}
export default LinkTest;
And inside my "Home" component:
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import Test from './Test';
class Home extends Component {
render() {
const {match} = this.props;
console.log(match.url);
return (
<Router>
<div>
<h2>
Hello from Home page
<Link to={`${match.url}/Test`}>Link To Test</Link>
<Route path={`${match.url}/Test`} component={Test}/>
</h2>
</div>
</Router>
);
}
}
export default Home;
However:
When i click on the link inside "LinkTest" component (which was rendered earlier), the url on the browser is shown "http://localhost:3000/home/Test", but nothing happens.
When i clicked on the link inside "Home" component, (which was rendered the same time as the "LinkTest" using the same link), it showed the same url on the browser: "http://localhost:3000/home/Test", only this time it rendered the "Test" component.
Why does this happen? (what i want to achieve is I want to use the link inside "LinkTest" to render "Test" component inside "Home" component, or something similar).
I hope this is clear enough.
You can do it in following way:
<Route exact path='/a' component={A} />
<Route path='/b' component={B} />
// Following should be router inside Component B
<Route exact path='/b/abc' component={OtherComponent}
If you want I've prepared few react-router 4 examples. These are hosted here. https://github.com/SandipNirmal/react-router-examples
If you need Nested routing inside ComponentB you should add Links for those Routes as well. And use match.url and match.path to build the nested Links and Routes.
const ComponentB = ({ match }) => {
return (
<div>
<div>
<ul>
<li><Link to={`${match.url}/c`}>Component C</Link></li>
// more Links
<li><Link to={`${match.url}/css`}>CSS</Link></li>
<li><Link to={`${match.url}/react`}>React</Link></li>
</ul>
</div>
<div>
<Route path={`${match.path}/c`} component={ComponentC} />
// more nested Routes
<Route path={`${match.path}/css`} render={() => { return <h1>CSS</h1> }}/>
<Route path={`${match.path}/react`} render={() => { return <h1>React</h1> }}/>
</div>
</div>
);
}
Nested routing
Components created via Route will automatically be passed the following prop objects: match, location and history.
Using match you can implement nested Routes. The match object contains the following properties:
params — (object) Key/value pairs parsed from the URL corresponding
to the dynamic segments of the path
isExact — (boolean) true if the entire URL was matched (no trailing characters)
path — (string) The path pattern used to match. Useful for building nested Routes
url — (string) The matched portion of the URL. Useful for building
nested Links
Reference
https://medium.freecodecamp.org/beginners-guide-to-react-router-4-8959ceb3ad58
https://medium.com/#pshrmn/a-simple-react-router-v4-tutorial-7f23ff27adf
I cannot get activeClassName to render correctly in my Navigation component Link. This has been tracked down to the following symptoms:
In the following code, the Navigation component is not getting a props.route passed to it at all. The App component has a props.route, however it is never updated as the user navigates to other routes. It is always set to the first route that was loaded. The componentWillReceiveProps is fired when changing routes, as the props.children is changing.
Here are the relevant snippets of my files:
app.jsx
import router from 'app/router';
[...]
ReactDOM.render(
<Provider store={store}>
{router}
</Provider>,
document.getElementById('app')
);
router/index.jsx
export default (
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={GameBoard}/>
<Route path="profile" component={ProfileBoard}/>
<Route path="profile/:userId" component={ProfileBoard}/>
<Route path="help" component={Help}/>
<Route path="about" component={About}/>
</Route>
</Router>
);
App.jsx
import React from 'react';
import Navigation from 'Navigation';
export default class App extends React.Component {
static propTypes = {};
constructor(props) {
super(props);
}
render() {
return (
<div>
<Navigation />
<div className="content">
{this.props.children}
</div>
</div>
);
}
};
Navigation.jsx
import React from 'react';
import {connect} from 'react-redux';
import {IndexLink, Link} from 'react-router';
export class Navigation extends React.Component {
static propTypes = {};
constructor(props) {
super(props);
}
render() {
return (
<div className="top-bar navigation">
<div className="row">
<div className="small-12 columns">
<div className="top-bar-left">
<ul className="menu">
<li className="menu-text">
TVDeadpool.xyz
</li>
<li>
<IndexLink to="/" activeClassName="link-active">Bets</IndexLink>
</li>
<li>
<Link to="/help" activeClassName="link-active">Help</Link>
</li>
<li>
<Link to="/about" activeClassName="link-active">About</Link>
</li>
</ul>
</div>
</div>
</div>
</div>
);
}
}
export default connect()(Navigation);
Navigation.jsx has been simplified. I removed some functionality that shows a Logout link if you are logged in, and handles that link. That is the reason I am including connect, though.
I am pouring through the documentation for react-router but cannot for the life of me figure out where I am going wrong. It must be something to do with nesting within the <Provider/>, I guess? Any help would be appreciated!
Note that if you want to see this in (in)action, check out TVDeadpool.xyz. Not a plug, just a fact.
UPDATE
Here is a hack fix:
App.jsx
import React from 'react';
import Navigation from 'Navigation';
export default class App extends React.Component {
static propTypes = {};
constructor(props) {
super(props);
}
render() {
return (
<div>
<Navigation location={this.props.location.pathname}/>
<div className="content">
{this.props.children}
</div>
</div>
);
}
};
Simply adding that location prop to <Navigation/> causes a re-render, without any additional code.
I think the reason this is happening is that App is always considered to be at route.path of "/", no matter what route is actually showing. Its immediate children seem to get the appropriate route.path, but Navigation, being a nested component of App, does not. In fact, it does not receive a route prop at all because it is not directly referenced by a <Route/>.
That said, how would this ever work? Should you not be able to simple include Link and expect it to work as described? I feel like I am missing something key to how react-router is supposed to work.
I think the reason activeClassName is not working is because you are using connect
export default connect()(Navigation);
See this issue... https://github.com/reactjs/react-redux/issues/388
It is reported as fixed in React Router 3.0.0-alpha.1 and newer.
Another hack fix I found you can use in older versions of react router is to pass {pure : false} to tell connect it is not a pure component...
export default connect(mapStateToProps, null, null, { pure: false })(Navigation);