How to pass component reference to routes component in react - javascript

In react i want to pass a reference of component to routes component . But when i route to path such as '/' it re render the passed component so starts with 0 again.
Here is a Link of Working example
https://codesandbox.io/s/884yror0p2
Here is a code, whenever i route from Home-->About the about counter starts with 0.
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class Dummy extends React.Component {
constructor(props) {
super(props);
this.state = {
c: 0
};
setInterval(() => {
this.setState({ c: this.state.c + 1 });
}, 1000);
}
render() {
return <h1> Counter {this.state.c}</h1>;
}
}
class BasicExample extends React.Component {
constructor(props) {
super(props);
this.state = {
// com: <Dummy /> this technique also not work
};
}
render() {
let com = <Dummy />;
return (
<div>
<Router>
<div>
{com}
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
</ul>
<hr />
<Route
exact
path="/"
// render={() => <Home com={com} />} //this also not work
component={props => <Home {...props} com={com} />}
/>
<Route path="/about"
component={props => <About {...props} com={com} />}
/>
</div>
</Router>
</div>
);
}
}
class Home extends React.Component {
render() {
return (
<div>
{this.props.com}
<h2>Home </h2>
</div>
);
}
}
class About extends React.Component {
constructor(props) {
super(props);
console.log(this.props);
}
render() {
return (
<div>
{this.props.com}
<h2>About</h2>
</div>
);
}
}
ReactDOM.render(<BasicExample />, document.getElementById("root"));

You can’t pass a component by “reference”. However, if you would like to have the same data between all the components you could initialize your state in the parent component (BasicExample) and pass it down or use a state container like Redux.

You can the component down but the component will also go through a mounting process that will invalidate the state that you have stored in it.
If you need to hold the state, it should be stored in the parent component.
const Router = ReactRouterDOM.HashRouter;
const Route = ReactRouterDOM.Route;
const Link = ReactRouterDOM.Link;
class Dummy extends React.Component {
constructor(props) {
super(props);
this.state = {
c: props.c || 0
};
}
componentWillReceiveProps(nextProps) {
if (this.props.c !== nextProps.c) {
this.setState({
c: nextProps.c
});
}
}
render() {
return <h1> Counter {this.state.c}</h1>;
}
}
class BasicExample extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 0
};
setInterval(() => {
this.setState(prevState => {
return {
counter: prevState.counter + 1
};
});
}, 1000);
}
render() {
let Com = <Dummy c={this.state.counter} />;
return (
<div>
<h2>Main App</h2>
<Router>
<div>
{Com}
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
</ul>
<hr />
<Route
exact
path="/"
// render={() => <Home com={com} />} //this also not work
render={props => (
<Home {...props} c={this.state.counter} Com={Dummy} />
)}
/>
<Route
path="/about"
render={props => (
<About {...props} c={this.state.counter} Com={Dummy} />
)}
/>
</div>
</Router>
</div>
);
}
}
class Home extends React.Component {
render() {
const Com = this.props.Com;
return (
<div>
<h2>Home </h2>
<Com c={this.props.c} />
</div>
);
}
}
class About extends React.Component {
constructor(props) {
super(props);
}
render() {
const Com = this.props.Com;
return (
<div>
<h2>About</h2>
<Com c={this.props.c} />
</div>
);
}
}
ReactDOM.render(<BasicExample />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router/4.2.0/react-router.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router-dom/4.2.2/react-router-dom.js"></script>
<div id="root"></div>
Few things to note here are:
Use render property of Route to pass in any props
When you use component (instead of render or children, below) the
router uses React.createElement to create a new React element from the
given component. That means if you provide an inline function to the
component prop, you would create a new component every render. This
results in the existing component unmounting and the new component
mounting instead of just updating the existing component. When using
an inline function for inline rendering, use the render or the
children prop
Use function version of setState to refer to prevState

Related

Unable to pass correct component as props to a custom router

I am trying to create a <ProtectedRoute /> component, in which I can pass a component like below
<Route exact={true} path={routes.LOGIN} component={Login} />
<ProtectedRoute path={routes.HOME} component={Home} authUser={this.state.authUser} />
<ProtectedRoute path={routes.PROJECT} component={Project} authUser={this.state.authUser} />
my ProtectedRoute.tsx code
interface ProtectedRouteProps {
path: string;
component: React.ElementType;
authUser: any;
}
export class ProtectedRoute extends React.Component<ProtectedRouteProps, {}> {
constructor(props: any) {
super(props);
}
render() {
const Component = this.props.component;
const authUser = this.props.authUser;
console.log(this.props.component);
return authUser ? (
<Component />
) : (
<Redirect to={{ pathname: routes.LOGIN }} />
);
}
}
in the props I have tried React.Component, React.ComponentType<any> too.
However, my app never routes to Project, it always routes to Home.
So, I added a log to see what I am getting as this.props.component.
I always get
class Home extends react__WEBPACK_IMPORTED_MODULE_0___default.a.Component {
constructor(props) {
super(props);
}
render() {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.c…
which makes me think I am not passing the Component properly.
Can anybody help with the mistake or how to pass a Component using a prop.
PS: If I add Project in Route instead of ProtectedRoute, it works fine.
EDIT: Adding Home/Project Component (Both of them have the same form).
export class Project extends React.Component {
constructor(props: any) {
super(props);
}
render() {
return (
<div>
<h1>Project</h1>
</div>
)
}
}
You need to send component
<Route exact={true} path={routes.LOGIN} component={Login} />
<ProtectedRoute path={routes.HOME} component={()=>(<Home/>)} authUser={this.state.authUser} />
<ProtectedRoute path={routes.PROJECT} component={()=>(<Project/>)} authUser={this.state.authUser} />
and the ProtectedRoute
interface ProtectedRouteProps {
path: string;
component: React.ElementType;
authUser: any;
}
export class ProtectedRoute extends React.Component<ProtectedRouteProps, {}> {
constructor(props: any) {
super(props);
}
render() {
const authUser = this.props.authUser;
console.log(this.props.component);
return authUser ? (
this.props.component()
) : (
<Redirect to={{ pathname: routes.LOGIN }} />
);
}
}

ReactJS - Issues about Dynamic Routes

I have these components. I want to turn every into a dynamic url. For example, when accessing in the browser, http://localhost:3000/houses/1 I want to appear the House 1.
The other things in the application are working fine. I just want to solve this problem of implementing dynamic routes.
The data is fetched from a json file
db.json file
[
{
"houseId": 1,
"name": "House 1",
"photos": [
"house1_001.jpg",
"house1_002.jpg",
"house1_003.jpg",
"house1_004.jpg"
]
},
{
"houseId": 2,
"name": "House 2",
"photos": [
"house2_001.jpg",
"house2_002.jpg",
"house2_003.jpg",
"house2_004.jpg"
]
},
{
"houseId": 3,
"name": "House 3",
"photos": [
"house3_001.jpg",
"house3_002.jpg",
"house3_003.jpg",
"house3_004.jpg"
]
}
]
Router Component
import React from 'react';
import { BrowserRouter as Router, Route, NavLink } from 'react-router-dom'
import App from './App'
import Intro from './Intro'
import Houses from './Houses'
import House from './House'
export default props => (
<Router>
<Route exact path='/' render={() => <App />} >
<Route exact path='/intro' render={() => <Intro />} />
<Route exact path='/houses' render={() => <Houses />} />
<Route exact path='/houses/:houseId' render={(props) => <House {...props} />} />
</Route>
</Router>
)
Houses Component
import React, { Component } from 'react'
import House from './House'
var data = require('./db.json');
class Houses extends Component {
constructor(props) {
super(props);
this.state = {
houses: []
};
}
componentDidMount() {
this.setState({
houses: data
})
}
render() {
const { houses } = this.state;
return (
<div className="content house">
{
houses.map((house, index) => {
return (
<div>
<House house={house} />
</div>
)
})
}
</div>
)
}
}
export default Houses
**House Component**
import React, { Component } from 'react';
class House extends Component {
constructor(props) {
super(props)
this.state = {
houseId: ""
}
}
componentDidMount() {
this.setState({
houseId: this.props.match.params.id
})
}
render() {
return (
<div>
<h3>{this.props.house.name}</h3>
<ul>
{this.props.house.photos.map((photo, index) => {
return (
<li><img src={`/images/${photo}`} /></li>
)
})
}
</ul>
</div>
)
}
}
export default House;
House component
import React, { Component } from 'react';
class House extends Component {
constructor(props) {
super(props)
this.state = {
houseId: ""
}
}
componentDidMount() {
this.setState({
houseId: this.props.match.params.id
})
}
render() {
return (
<div>
<h3>{this.props.house.name}</h3>
<ul>
{this.props.house.photos.map((photo, index) => {
return (
<li><img src={`/images/${photo}`} /></li>
)
})
}
</ul>
</div>
)
}
}
export default House;
Pass the json data to <House/> component and use the id to display the correct data.
import React, { Component } from 'react';
const data = require('./db.json');
class House extends Component {
constructor(props) {
super(props)
this.state = {
houses: data,
}
}
render() {
const houseId = this.props.match.params.houseId;
return (
<div>
<h3>{this.state.houses[houseId].name}</h3>
<ul>
{this.state.houses[houseId].photos.map((photo, index) => {
return (
<li><img src={`/images/${photo}`} /></li>
)
})
}
</ul>
</div>
)
}
}
export default House;
Create two components, one will be rendered in Houses and one will be render on house/1
// rendered inside Houses
class House extends Component {
render() {
return (
<div>
<h3>{this.props.house.name}</h3>
<ul>
{this.props.house.photos.map((photo, index) => {
return (
<li><img src={`/images/${photo}`} /></li>
)
})
}
</ul>
</div>
)
}
}
HouseInfo, which display data by query parameter
import React, { Component } from 'react';
const data = require('./db.json');
class HouseInfo extends Component {
constructor(props) {
super(props)
this.state = {
houses: data,
}
}
render() {
const id = this.props.match.params.houseId;
const houseId = id >= 1 ? id - 1 : 0;
return (
<div>
<h3>{this.state.houses[houseId].name}</h3>
<ul>
{this.state.houses[houseId].photos.map((photo, index) => {
return (
<li><img src={`/images/${photo}`} /></li>
)
})
}
</ul>
</div>
)
}
}
export default HouseInfo;
Router
import React from 'react';
import { BrowserRouter as Router, Route, NavLink } from 'react-router-dom'
import App from './App'
import Intro from './Intro'
import Houses from './Houses'
import House from './House'
import HouseInfo from './HouseInfo'
export default props => (
<Router>
<Route exact path='/' render={() => <App />} >
<Route exact path='/intro' render={() => <Intro />} />
<Route exact path='/houses' render={() => <Houses />} />
<Route exact path='/houses/:houseId' render={(props) => <HouseInfo {...props} />} />
</Route>
</Router>
)
Entire snippet is right except the thing is that you have wrongly matched the params id,
change the following code in house component
this.setState({
houseId: this.props.match.params.houseId
})
you have to use the same param id ie.,houseId inside the component
using the houseId in the state ie.,(this.state.houseId) in House component, loop through the json data and find the houseId and display the corresponding data.
I don't see what props you are passing to the House component but my guess is not exactly intended ones. Try this:
import { withRouter } from 'react-router-dom';
...
export default withRouter(Houses);
or without withRouter:
<Route exact path='/houses/:houseId' render={House} />
and in your Route your param value is specified as houseId, as it should be in House component:
this.setState({
houseId: this.props.match.params.houseId
})

Use a child's method in a parent component

This is my code:
NavigationComponent - this is my navigation. I want the button clicked here to fire up a method from the child.
class NavigationComponent extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentWillMount() {
firebase.auth().onAuthStateChanged(user => {
if (user) {
console.log(user);
this.setState(
{
user: user
}, () => this.props.checkUserState(this.state.user)
);
}
});
}
render() {
return (
<BrowserRouter>
<React.Fragment>
<Navbar>
<Navbar.Header>
<Navbar.Brand>
<Link id='home' to="/">UczIchApp</Link>
</Navbar.Brand>
</Navbar.Header>
<Nav>
<LinkContainer id='about' to='/about'>
<NavItem>O nas</NavItem>
</LinkContainer>
{
this.state.user ?
<React.Fragment>
<LinkContainer id="questions" to='/questions'>
<NavItem>Zadania</NavItem>
</LinkContainer>
<NavItem onClick={Want to use it here}>Wyloguj się</NavItem>
</React.Fragment>
:
<NavItem onClick={And here}>Zaloguj się</NavItem>
}
</Nav>
</Navbar>
<Switch>
<Route exact path="/about" component={AboutComponent}/>
<Route exact path="/questions" component={QuestionsComponent}/>
<Route exact path="/" component={HomeComponent}/>
<Route path='/question/:id' component={QuestionComponent}/>
</Switch>
</React.Fragment>
</BrowserRouter>
)
}
}
LogoutComponent - I want a method from this component to be fired up
export default class LogoutComponent extends react.Component {
constructor(p) {
super(p);
this.logout = this.logout.bind(this);
console.log('tada');
}
logout() {
console.log('got here');
firebase
.auth()
.signOut()
.then(() => {
this.setState({
user: null
}, function () {
this.props.checkUserState(this.state.user)
});
});
}
}
What I want to do, is to use the logout() function when the button on the navbar is clicked. The problem is I have no idea how to reference it.
I tried something like this LogoutComponent.logout, but no luck. If it's not possible, how could I solve this?
This can be done by using React refs, and a method on the child component.
parent.js
constructor() {
super();
this.child_ref = null;
}
createChildRef(DOMElement) {
this.child_ref = DOMElement;
}
respondToSomeEvent() {
if (this.child_ref) {
this.child_ref.somePublicMethod();
}
}
render() {
return (
<ChildComponent ref={this.createChildRef} />
)
}
child.js
class ChildComponent extends React.Component {
somePublicMethod() {
// The parent can call this method through the React ref
}
}
Sometimes it is necessary to do this, based on the architecture of your application, but you should look into passing around an EventEmitter instance instead, so that your components can respond to changes outside of state that is stored in React.

How to pass the state of the page to other React?

I want to know how I can pass a status from one page to another page for if used in the other way.
My first page Body.js (Which I handle the state):
import React from 'react';
import './Body.css';
import axios from 'axios';
import { Link } from "react-router-dom";
import User from './User';
class Body extends React.Component {
constructor (){
super();
this.state ={
employee:[],
employeeCurrent:[],
}
}
componentDidMount(){
axios.get('http://127.0.0.1:3004/employee').then(
response=>this.setState({employee: response.data})
)
}
getName = () => {
const {employee} = this.state;
return employee.map(name=> <Link className='link' to={`/user/${name.name}`}> <div onClick={()=>this.add(name)} key={name.id} className='item'> <img className='img' src={`https://picsum.photos/${name.name}`}></img> <h1 className='name'> {name.name} </h1></div> </Link>)
}
add = (name) => {
const nam = name;
this.state.employeeCurrent.push(nam)
console.log(this.state.employeeCurrent)
}
render(){
return(
<div className='body'>
{this.getName()}
</div>
)
}
}
export default Body;
My second page which I want to get the state called employeeCurrent:
import React from 'react';
import Header from './Header';
import Body from './Body';
class User extends React.Component {
constructor (props){
super(props);
this.props ={
employeeCurrent:[],
}
}
render(){
return(
<div >
{this.props.employeeCurrent}
</div>
)
}
}
export default User;
I'm using the React Router, it looks like this:
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import './App.css';
import Home from './Home';
import User from './User';
const AppRouter = () => (
<Router>
<div className='router'>
<Route exact path="/" component={Home}/>
<Route path="/user/:id" component={User}/>
</div>
</Router>
);
export default AppRouter;
My project is:
Home page, where you have users, obtained from the API, all users have attributes (name, age, city and country). Saved in employeeCurrent variable:
What I want is: grab these attributes from the clicked user and play on the user page:
Someone would can help me PLEASE?????
Like I explained earlier, you need to lift the state up:
AppRouter (holds the state and passes it to children)
class AppRouter extends React.Component {
state = {
employeeCurrent: [],
employee: []
};
componentDidMount() {
axios
.get("http://127.0.0.1:3004/employee")
.then(response => this.setState({ employee: response.data }));
}
add = name => {
this.setState(prevState => {
const copy = prevState.employeeCurrent.slice();
copy.push(name);
return {
employeeCurrent: copy
};
});
};
render() {
return (
<Router>
<div className="router">
<Route
exact
path="/"
render={props => (
<Home
{...props}
add={this.add}
employee={this.state.employee}
currentEmployee={this.state.currentEmployee}
/>
)}
/>
<Route
path="/user/:id"
component={props => (
<User
{...props}
employee={this.state.employee}
currentEmployee={this.state.currentEmployee}
/>
)}
/>
</div>
</Router>
);
}
}
Body and User (receive parent state as props together with updater functions):
class Body extends React.Component {
getName = () => {
const { employee, add } = this.props;
return employee.map(name => (
<Link className="link" to={`/user/${name.name}`}>
{" "}
<div onClick={() => add(name)} key={name.id} className="item">
{" "}
<img
className="img"
src={`https://picsum.photos/${name.name}`}
/>{" "}
<h1 className="name"> {name.name} </h1>
</div>{" "}
</Link>
));
};
render() {
return <div className="body">{this.getName()}</div>;
}
}
class User extends React.Component {
render() {
// you will need to map employeeCurrent somehow
return <div>{this.props.employeeCurrent}</div>;
}
}

how to send value one component to another component in react js?

could you please tell me how to send input field value on second component on button click .I have one button and input field in first component.On button click I need to send input field value to second component
here is my code
http://codepen.io/naveennsit/pen/GZbpeV?editors=0010
var { Router, Route,browserHistory } = ReactRouter
class First extends React.Component{
sendValue(){
browserHistory.push('/second');
}
render(){
return (<div>
<input type='text' />
<button onClick={this.sendValue}>send</button>
</div>)
}
}
class Second extends React.Component{
render(){
return <div>second component</div>
}
}
class Main extends React.Component{
render(){
return (
<Router history={browserHistory}>
<Route path='/' component={First}></Route>
<Route path='/second' component={Second}></Route>
</Router>)
}
}
React.render( <Main />,document.getElementById('app'));
browserHistory.push('/')
The best solution would be to create some architecture that would allow you to have a separate state object, change it, and pass changes on to your components. See Flux, or Redux.
For a pinpointed solution, you could use url params:
class First extends React.Component{
sendValue(){
browserHistory.push('/second/' + this.refs.textField.value);
}
render(){
return (
<div>
<input type='text' ref='textField' />
<button onClick={() => {this.sendValue()}}>send</button>
</div>)
}
}
class Second extends React.Component {
render(){
return <div>second component: {this.props.params.test}</div>
}
}
class Main extends React.Component{
render(){
return (
<Router history={browserHistory}>
<Route path='/' component={First}></Route>
<Route path='/second/:test' component={Second}></Route>
</Router>)
}
}
thanks #omerts, by using refs is a best idea and also another way using state and i am also new to react.
var { Router, Route, browserHistory } = ReactRouter
class First extends React.Component{
constructor(props){
super(props);
this.sendValue = this.sendValue.bind(this);
this.setValue = this.setValue.bind(this);
this.state = {
userID: "#nageshwar_uidev"
};
}
setValue(e){
this.setState({
userID: e.target.value
});
}
sendValue(){
//console.log(this.state.userID);
browserHistory.push('/second/'+this.state.userID);
}
render(){
return (
<div>
<input type='text' value={this.state.userID} onChange={this.setValue} />
<button onClick={this.sendValue}>send</button>
</div>
)
}
}
class Second extends React.Component{
render(){
let { userID } = this.props.params;
return <div>The userID from first component is {userID} <a href="#" onClick={()=>browserHistory.push('/')}>back</a></div>
}
}
class Main extends React.Component{
render(){
return (
<Router history={browserHistory}>
<Route path='/' component={First}></Route>
<Route path='/second/:userID' component={Second}></Route>
</Router>)
}
}
React.render( <Main />,document.getElementById('app'));
browserHistory.push('/')
working example at codepen

Categories

Resources