I am newbie in react. I read this question , but didn't solve my problem. I think I'm wrong with componentWillReceiveProps comprehension . My component and it's parent are like this:
the parent:
import React from 'react';
import { Router, Route, Link } from 'react-router';
export default class App extends React.Component {
items = [
{id: 1086, title: 'MENU1'},
{id: 1083, title: 'MENU2'},
{id: 1093, title: 'MENU3'}]
renderItems = ()=> {
return this.items.map((item, index)=> {
let url = 'content/' + item.id;
return (<li key={index}><Link to={url} activeClassName="active">{item.title}</Link></li>);
});
}
render() {
return (
<div>
<h1 style={{textAlign:'center'}}>My First React App</h1>
<ul className="nav nav-pills nav-justified">{this.renderItems()}</ul>
{this.props.children}
</div>
);
}
}
the child:
import React, { Component } from 'react';
import UmContent from './UmContent'
import $ from 'jquery';
import ProgressBar from 'progressbar.js';
export default class UmContentContainer extends Component {
state = {
data: ''
}
firstInitial = true;
componentDidMount() {
if (this.firstInitial) {
let props = this.props;
this.sendRequestToServer(props);
this.firstInitial = false;
}
}
sendRequestToServer = (props)=> {
if (!props || !props.params || !props.params.id) {
return;
}
let itemId = props.params.id;
let that = this;
this.setState({data: itemId});
}
componentWillReceiveProps(props, states) {
this.sendRequestToServer(props);
console.log(props.params.id);
}
render() {
return (
<div className="col-md-12">
<h1>{this.state.data}</h1>
</div>);
}
}
and index.js :
import React from 'react'
import { render } from 'react-dom'
import { Router, Route, Link,hashHistory } from 'react-router'
import App from './App';
import UmContentContainer from './Umbreco/UmContentContainer';
render((
<Router history={hashHistory}>
<Route path="/" component={App}>
<Route path="/content/:id" component={UmContentContainer} />
</Route>
</Router>
), document.getElementById('root'))
after first mounting , console writes "id" twice. I can't understand why?
I found my answer HERE , I was wrong , actually it was about the hashHistory and pushing new url . not componentWillReceiveProps.
Related
I am trying to render props from the Room to the single room page. but when I try to access the props I get an empty object.
The below code snippet is the room component.
import React, { Component} from 'react';
import defaultImg from '../images/room-1.jpeg';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
export default function Room({ room }) {
const { name, slug, images, price } = room;
// console.log(slug);
return (
<article className='room'>
<div className='img-container'>
<img src={images[0] || { defaultImg }} alt='single room' />
<div className='price-top'>
<h6>${price}</h6>
<p>per night</p>
</div>
<Link to={`/rooms/${slug}`} className='btn-primary room-link'>
Features
</Link>
</div>
<p className='room-info'>{name}</p>
</article>
)
}
Room.propTypes = {
room: PropTypes.shape({
name: PropTypes.string.isRequired,
slug: PropTypes.string.isRequired,
images: PropTypes.arrayOf(PropTypes.string).isRequired,
price: PropTypes.number.isRequired,
})
}
Above is the parent element that is passing down the slug(prop) property
Now this is the single room that is trying to access the props passed from room.js
import React, { Component } from 'react'
import defaultBcg from '../images/room-1.jpeg'
import Hero from '../Components/Hero'
import Banner from '../Components/Banner'
import { Link } from 'react-router-dom'
import { RoomContext } from '../Context'
export default class SingleRooms extends Component {
constructor(props) {
super(props)
console.log(this.props);
this.state = {
slug: this.props.match.params.slug,
defaultBcg
};
}
static contextType = RoomContext;
render() {
const { getRoom } = this.context;
const room = getRoom(this.state.slug);
console.log(room);
return (
<div>SingleRooms</div>
)
}
}
But when I try to log the props I get an empty object, I get this error
"SingleRooms.js:13 Uncaught TypeError: Cannot read properties of undefined (reading 'params')" error
Here is My app Js using routes
import Home from "./Pages/Home";
import Error from "./Pages/Error";
import Rooms from "./Pages/Rooms";
import SingleRooms from "./Pages/SingleRooms";
import { Route, Routes } from "react-router-dom";
import Navbar from "./Components/Navbar";
import './App.css';
function App() {
return (
<>
<Navbar/>
<Routes>
<Route path='/' element={<Home />} />
<Route path='/rooms' element={<Rooms />} />
<Route path="/rooms/:slug" element={<SingleRooms />} />
<Route path="*"element={<Error/>}/>
</Routes>
</>
);
}
export default App;
I was wondering if it is possible to pass an object as a parameter to a react-router-dom endpoint using react-router-dom-v6. I've seen some ways of doing it with Hooks but they require functions instead of components.
This is the main component handling the Routes
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { Home } from "./Home";
import { Page01 } from "./Page01";
import { Page02 } from "./Page02";
type TypeProps = { name:string }
type TypeState = { }
class Main extends React.Component<TypeProps, TypeState> {
constructor(props:TypeProps) {
super(props);
this.state = { };
}
render() {
return (
<div>
<BrowserRouter>
<Routes>
<Route path="/" element={ <Home name="home"/> }/>
<Route path="/page01" element={ <Page01 name="page01"/> }/>
<Route path="/page02" element={ <Page02 name="page02"/> }/>
</Routes>
</BrowserRouter>
</div>
);
}
}
ReactDOM.render(<Main name="main" />, document.getElementById("main"));
Then a basic component which is only handling the Link to method.
import React from "react";
import ReactDOM from "react-dom";
import { Link } from "react-router-dom";
type TypeProps = { name:string }
type TypeState = { }
class Page01 extends React.Component<TypeProps, TypeState> {
constructor(props:TypeProps) {
super(props);
this.state = { };
}
render() {
return (
<div>
<Link to={ "/page02" }>
<span>Go To Page 02</span>
</Link>
</div>
);
}
}
export { Page01 }
And then an almost identical component at the other end which will need to receive this data.
import React from "react";
import ReactDOM from "react-dom";
import { Link } from "react-router-dom";
type TypeProps = { name:string }
type TypeState = { }
class Page02 extends React.Component<TypeProps, TypeState> {
constructor(props:TypeProps) {
super(props);
this.state = { };
}
render() {
return (
<div>
<Link to={ "/page01" }>
<span>Go To Page 01</span>
</Link>
</div>
);
}
}
export { Page02 }
I've tried to pass the data with state
let paramData = {
field01:"data01",
field02:"data02"
}
<Link to={ "/page02" } state={{ paramData:paramData }}>
<span>Go To Page 02</span>
</Link>
But I cannot receive it on the other end because the I'm using a component and not a function
React Router provide special props at Route component.
On props location you can get property state contain data from Link state
at constructor of Page02 you can get state with:
constructor(props: TypeProps) {
super(props);
this.state = {};
console.info(props);
console.info(props.location);
console.info(props.location.state);
}
Can attempt use lifecycle method componentWillReceiveProps
componentWillReceiveProps(nextProps) {
console.log(nextProps);
console.log(nextProps.location);
}
I think you route Page02 component has not good props type,
react-router-dom export interface RouteComponentProps
should be use with Route Component
import React from "react";
import ReactDOM from "react-dom";
import { Link, RouteComponentProps} from "react-router-dom";
type TypeProps = { name:string }
type TypeState = { }
type Page02Props = RouteComponentProps<TypeProps>;
class Page02 extends React.Component<Page02Props, TypeState> {
constructor(props:TypeProps) {
super(props);
this.state = { };
console.log(props); // should be {location: [Object], match: [Object], history: [Object], name: string }
}
render() {
return (
<div>
<Link to={ "/page01" }>
<span>Go To Page 01</span>
</Link>
</div>
);
}
}
export { Page02 }
I dont find official documentation for react-router-dom + typescript,
but on a other topic
can get special props with good type Route Component
I'm following this React Router 4 tutorial to add routes to my Meteor / React project:
https://medium.com/#pshrmn/a-simple-react-router-v4-tutorial-7f23ff27adf
When I click links, the URL changes as expected but the route content doesn't appear. If I refresh the page then the route content loads correctly. Here's my code:
Versions: METEOR#1.6.0.1, react-router-dom#4.2.2
main.js
``` JSX
import React from 'react'; // eslint-disable-line no-unused-vars
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
// Start the app with imports
import '../imports/startup/accounts-config.js';
import App from '../imports/ui/layouts/App.jsx';
Meteor.startup(() => {
render((<BrowserRouter><App /></BrowserRouter>), document.getElementById('app'));
});
```
App.js
``` JSX
import React, { Component } from 'react';
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
// game components
import { MapSquares } from '../../api/map/map.js';
// User Accounts
import AccountsUIWrapper from '../AccountsUIWrapper.jsx';
import { Switch, Route, Link } from 'react-router-dom';
import Home from '../pages/home';
import Roster from '../pages/roster';
import Schedule from '../pages/schedule';
function MapSquare(props) {
return (
<span className="mapsquare" key={props.index}>{props.row}, {props.column}</span>
);
}
class App extends Component {
renderMapSquare(mapsquare) {
return (
<MapSquare
index={mapsquare._id}
/>
);
}
render() {
// avoid error while loading data
let data = [];
if (typeof this.props.mapsquares !== 'undefined') {
data = this.props.mapsquares;
}
let rows = [];
let index = 0;
if (data.length !== 0) {
for (let i = 0; i < 5; i++) {
let thisrow = [];
for (let j = 0; j < 2; j++) {
let source = data[index];
let newthing = <MapSquare
key={source._id}
row={source.row}
column={source.column}
/>;
thisrow.push(newthing);
index++;
}
rows.push(<div className="map-row" key={i}>{thisrow}</div>);
}
}
return (
<div className="game">
<AccountsUIWrapper />
<nav>
<ul>
<li><Link to='/'>Home</Link></li>
<li><Link to='/roster'>Roster</Link></li>
<li><Link to='/schedule'>Schedule</Link></li>
</ul>
</nav>
<Switch>
<Route exact path='/' component={Home}/>
<Route path='/roster' component={Roster}/>
<Route path='/schedule' component={Schedule}/>
</Switch>
<div className="game-map">
{rows}
</div>
</div>
);
}
}
// provides data to component App
export default withTracker(() => {
Meteor.subscribe('mapsquares');
return {
'mapsquares': MapSquares.find({}, { 'sort': { 'row': 1, 'column': 1 } }).fetch(),
};
})(App);
```
home.js (the roster and schedule pages work the same way)
``` JSX
import React from 'react';
const Home = () => (
<div>
<h1>Home</h1>
</div>
);
export default Home;
```
Any idea what I'm missing here? It so nearly works!
Wrapping my data container in withRouter seems to have done the trick:
App.js:
``` JSX
import { withRouter, Switch, Route, Link } from 'react-router-dom';
...
export default withRouter(withTracker(() => {
Meteor.subscribe('mapsquares');
return {
'mapsquares': MapSquares.find({}, { 'sort': { 'row': 1, 'column': 1 } }).fetch(),
};
})(App));
```
See the docs:
https://reacttraining.com/react-router/web/api/withRouter
I'm having trouble with react-router-dom Link component only changing the URL and not updating the route.
It works fine linking from /courses -> /courses/<course-slug>
and then /courses/<course-slug> -> /lessons/<lesson-slug>
However it the I'm having problems linking to other lessons e.g. from /lessons/<lesson-slug> -> /lessons/<different-lesson-slug>
It seems like it only updates the LessonsList component and updates the URL but doesn't update the route / content or parent Lesson.
I've made sure to pass match, location props down to the component incase it had anything to do with update blocking - https://reacttraining.com/react-router/web/guides/dealing-with-update-blocking but it still doesn't seem to work.
I can't see where I'm going wrong, and it should be relatively simple.
Does it have anything to do with how I've set up my routes or the routes having the same?
Here's my dependencies and code, any point in the right direction would be appreciated.
"dependencies": {
"axios": "^0.16.2",
"prop-types": "^15.5.10",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-router-dom": "^4.1.2"
}
index.js
import css from '../css/index.scss';
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import App from './components/App';
ReactDOM.render(
<Router>
<App />
</Router>
, document.getElementById('app'));
App/index.js
import React, { Component } from 'react';
import Header from '../Header';
import Main from '../Main';
import Footer from '../Footer';
class App extends Component {
render() {
return(
<div>
<Header />
<Main />
<Footer />
</div>
);
}
}
export default App;
Main/index.js
import React, { Component } from 'react';
import { Route, Link, Switch } from 'react-router-dom';
import Home from '../Home';
import Courses from '../Courses';
import Course from '../Course';
import Lessons from '../Lessons';
import Lesson from '../Lesson';
class Main extends Component {
constructor(props) {
super(props);
}
render() {
return(
<div className="main">
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/courses/:course" component={Course}/>
<Route exact path="/courses" component={Courses}/>
<Route path="/lessons/:lesson" component={Lesson}/>
<Route exact path="/lessons" component={Lessons}/>
<Route render={ () => (
<div>Not Found 404</div>
)}/>
</Switch>
</div>
);
}
}
export default Main;
Lessons/index.js
import React, { Component } from 'react';
import api from '../../utils/api';
import LessonsList from '../LessonsList';
import { Link } from 'react-router-dom';
class Lessons extends Component {
constructor(props) {
super(props);
this.state = {
lessons: null
};
}
componentDidMount() {
api.getAllLessons()
.then((lessons) => {
this.setState({
lessons: lessons
});
});
}
render() {
return(
<div className="lessons">
{!this.state.lessons ?
<div>Loading...</div>
:
<div>
<LessonsList
lessons={this.state.lessons}
{...this.props}
/>
</div>
}
</div>
);
}
}
export default Lessons;
Lesson/index.js
import React, { Component } from 'react';
import api from '../../utils/api';
import LessonsList from '../LessonsList';
import { Link } from 'react-router-dom';
class Lesson extends Component {
constructor(props) {
super(props);
this.state = {
lesson: null
}
}
componentDidMount() {
api.getLesson(this.props.match.params.lesson)
.then((lesson) => {
this.setState({
lesson: lesson[0]
});
});
}
render() {
return(
<div className="lesson">
{!this.state.lesson ?
<div>Loading...</div>
:
<div>
<h3>Course title: {this.state.lesson.course.title}</h3>
<h1>Lesson: {this.state.lesson.title}</h1>
<h2>Other lessons from this course</h2>
<LessonsList
lessons={this.state.lesson.lessons}
{...this.props}
/>
</div>
}
</div>
);
}
}
export default Lesson;
LessonsList/index.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
function LessonsList(props) {
return(
<ul>
{props.lessons.map((lesson) => {
return(
<li key={lesson.id}>
<Link to={`/lessons/${lesson.slug}`}>{lesson.title}</Link>
</li>
);
})}
</ul>
);
}
LessonsList.propTypes = {
lessons: PropTypes.array.isRequired
}
export default LessonsList;
UPDATE:
Here's the updated Component with componentWillReceiveProps
Lesson/index.js
import React, { Component } from 'react';
import api from '../../utils/api';
import LessonsList from '../LessonsList';
import { Link } from 'react-router-dom';
class Lesson extends Component {
constructor(props) {
super(props);
this.state = {
lesson: null
}
}
componentDidMount() {
api.getLesson(this.props.match.params.lesson)
.then((lesson) => {
this.setState({
lesson: lesson[0]
});
});
}
componentWillReceiveProps(nextProps) {
if(this.props.match.params.lesson !== nextProps.match.params.lesson) {
api.getLesson(nextProps.match.params.lesson)
.then((lesson) => {
this.setState({
lesson: lesson[0]
});
});
}
}
render() {
return(
<div className="lesson">
{!this.state.lesson ?
<div>Loading...</div>
:
<div>
<h3>Course title: {this.state.lesson.course.title}</h3>
<h1>Lesson: {this.state.lesson.title}</h1>
<h2>Other lessons from this course</h2>
<LessonsList
lessons={this.state.lesson.lessons}
{...this.props}
/>
</div>
}
</div>
);
}
}
export default Lesson;
Your <Lesson /> component only sets the lesson during the componentDidMount lifecycle hook. If you're on a lesson and you change the slug, it does not cause the component to remount. You can use the componentWillReceiveProps lifecycle hook to accomplish what you're after.
componentWillReceiveProps(nextProps) {
api.getLesson(nextProps.match.params.lesson)
.then((lesson) => {
this.setState({
lesson: lesson[0]
});
});
}
This issue with componentDidMount event. It's not fired when you change Lesson. So you should use componentDidUpdate to change a lesson.
componentDidUpdate() {
if (this.props.match.params.lesson !== this.state.lesson.slug)
api.getLesson(this.props.match.params.lesson)
.then((lesson) => {
this.setState({
lesson: lesson[0]
});
});
}
}
Hi I am brand new in react. I am trying to pass data from server to client via React universal rendering. But I am in now luck. Still I can render the html but I am not able to access data through this.props.data
Here is my code
// server.js
app.get('*', (req, res) => {
match({
routes: (<Router>{routes}</Router>),
location: req.url
},
(err, redirect, renderProps) => {
if (err) {
return res.status(500).send(err.message);
} else if (redirect) {
return res.redirect(302, redirect.pathnam + redirect.search);
} else if (renderProps.components.some(component => component === NotFoundPage)) {
res.status(404);
}
let data = [
{
title: 'Godfather'
},
{
title: 'Godfather 2'
}
];
data = JSON.stringify(data);
const renderedApp = renderToString(
<DataWrapper data={data}>
<RouterContext {...renderProps} />
</DataWrapper>
);
res.render('index.ejs', {
renderedApp
});
});
});
// routes.js
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import HomePage from './components/routes/HomePage';
import MovieDetailsPage from './components/routes/MovieDetailsPage';
import NotFoundPage from './components/routes/NotFoundPage';
import App from './components/app';
export {
NotFoundPage
};
export default (
<Route path="/">
<IndexRoute component={HomePage} />
<Route path="movie" component={MovieDetailsPage} />
<Route path="*" component={NotFoundPage} />
</Route>
);
// client.js
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, browserHistory } from 'react-router';
import routes from './routes';
import DataWrapper from '../src/DataWrapper';
let data = [];
ReactDOM.render(
<DataWrapper data={data}>
<Router history={browserHistory} routes={routes} onUpdate={window.scrollTo(0, 0)}/>
</DataWrapper>,
document.getElementById('app')
);
// DataWrapper
import React, { Component } from 'react';
class DataWrapper extends Component {
getChildContext () {
return {
data: this.props.data
};
}
render () {
return this.props.children;
}
}
DataWrapper.childContextTypes = {
data: React.PropTypes.oneOfType([
React.PropTypes.object,
React.PropTypes.array
]).isRequired
};
export default DataWrapper;
// HomePage.js where I want to access the data in this.props.data
import React, { Component } from 'react';
export default class HomePage extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data
};
}
render() {
return (
<div>
<h1>{this.state.data}</h1>
</div>
);
}
}
Can someone please point me in the right direction and explain if possible what I am doing wrong
Thank you
You're currently rendering the children in DataWrapper, but you're not passing in the props.
Not sure if this is the optimal way to do this, but here's how I get the props and state passed in
{this.props.children && React.cloneElement(this.props.children, {
state: this.state
})}
In this use case, I suggest using Redux instead of writing the custom data wrapper. Because
Redux can make the single source of truth, all the components can refer to that, no matter which level it's in (parent or childen) and where is the page rendered (server or client). It can help you to make the architect clearer even it introduces more overhead.
Here's some pseudo code I used before:
// server.js
// configureStore will create a empty store here.
const store = configureStore();
// Then use store.setState or any middleware to prepopulate the store.
....
const jsx = (
<ReduxProvider store={store}>
<StaticRouter context={context} location={req.url}>
<App/>
</StaticRouter>
</ReduxProvider>
);
const html = '<!doctype html><html><head></head>
<body>
<script>
window.REDUX_DATA = ${ JSON.stringify(store.getState()) }
</script>
<div id="root">${ReactDOM.renderToString(jsx)}</div>
</body>
</html>'
res.send(html)
//client.js
const render = App => {
// configStore will create store in client side with initial state passed from server.
const store = configStore(window.REDUX_DATA);
return ReactDOM.hydrate(
<ReduxProvider store={store}>
<BrowserRouter>
<App/>
</BrowserRouter>
</ReduxProvider>,
document.getElementById('root')
);
};
//HomePage.js
class HomePage extends React.Component {
render() {
return (
<div>
this.props.foo.value
</div>
)
}
const mapStateToProps = (state) => ({foo: state.foo});
export default connect(mapStateToProps)(HomePage);
}