I build a project for a school’s records system, in which I build the front-end with React. On the main component of the admin page, I wanted to have a react-router which will navigate through the admin dialogs. As I tried to implement this, the following problem occurred: when trying to pass parameters to a class through the react route component, the child component receives no props.
I have the following react component hierarchy:
class Test extends React.Component {
constructor() {
super();
console.log("in class: " + this.props)
}
render() { return <div>test</div>}
}
class AdminPage extends BasicPage {
/* Other class functions here... */
render() {
let pageBody = "";
if(!this.state.isLoading)
pageBody = (
<Router>
<Switch>
<Route path={"/:schoolName/admin"} component={AdminMenu} exact/>
<Route path={"/:schoolName/admin/view/:id"} exact
component={() => <Test par1="abc" />} />
</Switch>
</Router>
);
return (
<Layout title={ this.state.isLoading ?
TITLE_UNTIL_LOADED :
PAGE_TITLE + this.state.schoolPrefs.heb_name}
subtitle={ this.state.subtitle }
notification={ this.state.notification }
isLoading={ this.state.isLoading }
>
{pageBody}
</Layout>
);
}
}
When I go to /Random Name/admin/view/someID, it prints to the console in class: undefined.
I then wanted to see if the problem is in the passing component or the receiving one. I defined the function otherTest(props) as follows:
function otherTest(props) {
console.log("Function props: " + props);
return (<Test {...props} />);
}
And then changed the route component like so:
<Route path={"/:schoolName/admin/view/:id"} exact
component={otherTest} />
When then I went to /Random Name/admin/view/someID, I saw that the function received the props just fine, but the log within <Test … /> still printed undefined.
I also tried adding <Test param1=”123” /> after the {pageBody} variable in the main render function, but it printed in class: undefined as well.
Does someone know where the problem might be?
Thanks.
You must take props parameter from constructor and then pass it to super.
constructor(props){
super(props);
}
Do not use this.props in constructor beacuse constructor fire only at the creating class moment.
Use this code:
constructor(props) {
super(props);
console.log(props);
}
Related
In my reactjs project, component is rendered twice. if I remove the componentDidMount, the problem is fixed. But I need it in my project. I tried the solutions on the internet, but I couldn't.
App.js
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
users: []
};
}
componentDidMount() {
fetch("https://reqres.in/api/users?page=2")
.then(res => res.json())
.then(result => {
this.setState({
users: result.data
});
});
}
render() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" render={() => <Home />} />
</Switch>
</BrowserRouter>
);
}
}
Home.js
export default class Home extends Component {
render() {
console.log("Render");
return (
<div>
<h1>console.log render twice</h1>
</div>
);
}
}
https://codesandbox.io/s/wyjk931z6l
console.log works on Home.js twice.
You App component re-renders because the API call that you make in componentDidMount results in a setState on success. Due to this, the child components also go though a re-render even though their props didn't change. In order to avoid it, you can write the child component by extending React.PureComponent which implements the shouldComponentUpdate by shallowly comparing the props and state.
export default class Home extends PureComponent {
render() {
console.log("Render");
return (
<div>
<h1>console.log render twice</h1>
</div>
);
}
}
Working demo
So I've been learning React for a while, but one of the key's in what I have yet succeeded is passing states.
What I'm trying to do is passing a state from my App.js to a component rendered via route. Unfortunately, in MyComponent.js, I get error message 'lang is undefined'.
Can someone guide me to a solution?
This is what I have so far:
App.js
class App extends Component {
constructor (props) {
super (props)
this.state = {
language: 'en'
}
}
render() {
return (
<BrowserRouter>
<div className='App'>
<Switch>
<Route
exact path='/' render={(props) => <MyComponent lang={this.state.language} />}
/>
<Route exact path='/privacy/policy' component={Policy} />
<Route path='*' component={NotFound} />
</Switch>
</div>
</BrowserRouter>
);
} } export default App;
MyComponent.js
export class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
lang : {lang}
}
}
render () {
return (
<div className='searchBar'>
<p>Current language is: {this.state.lang}</p>// HERE COMES THE STATE
</div>
);
}}
You are trying to pass props down to MyComponent component but you are trying to render it in SearchBar component. Rename SearchBar to MyComponent in the first route of your App.js.
Also, in MyComponent.js, remove the lang property from state and change:
<p>Current language is: {this.state.lang}</p>
to
<p>Current language is: {this.props.lang}</p>
I'm using react router V4, when i declare a Route i want wrap my component inside a High Order component, if I use HOC in
export default hoc(Component)
Then I put the component in the render prop, it works.
When I do this
`<Route exact path="/projects" render={(props) => (withNavHOC(<ProjectsContainer {...props}/>))} />`
It returns this error:
Uncaught Error: Route.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
Why it's happening? My Hoc works fine, it returns a valid react component:
`
const withNavHOC = (WrappedComponent) => {
return class extends React.Component{
render(){
if(this.props.match.params.id){
console.log("Here");
return(
<div>
<ProjectMenu/>
<WrappedComponent {...this.props}/>
</div>)
}
return(
<div>
<Navigation/>
<WrappedComponent {...this.props}/>
</div>
)
}
}
};`
To HOC which is of course a simple function, we must pass "Component", not <Component/>.
I agree there are multiple questions asked for the same. I did a lot of research for a few hours, however, I couldnt resolve this easy looking error. According to How to pass props to {this.props.children} post, i understand the easy use of React.cloneElement and React.Children.
Following is my Parent Class:
class AboutPage extends React.Component {
constructor(props, context){
super(props, context);
this.state = {
details: "details"
}
}
render() {
const childrenWithProps = React.Children.map(this.props.children,
(child) => {
React.cloneElement(child, {
details: this.state.details
})
}
);
return (
<div className="jumbotron">
<h1>About</h1>
<p>This application uses React, Redux, React Router and other libs for our EducationOne Project</p>
<Link to="/about/Owner">
<Button color="primary">test</Button>
</Link>
{childrenWithProps}
</div>
);
}
}
AboutPage.PropTypes = {
children: PropTypes.object.isRequired
};
Following is my child component:
const Owner = (props) => {
return (
<div>Owner details: {props.details}</div>
);
};
Instead of importing the child or parent, i nested the route in my routes.js to create a child for aboutPage:
export default (
<Route path="/" component={App}>
<IndexRoute component={Login} />
<Route path="home" component={HomePage}/>
<Route path="about" component={AboutPage}>
<Route path="omkar" components={Omkar} />
</Route>
<Route path="courses" component={CoursesPage}>
{/*<IndexRoute components={CourseDetailsAndList}/>*/}
</Route>
</Route>
);
However, I dont see any error or any message in the console, nor the child component loaded when i click the button to load the child component.
Any help will be really appreciated.
The problem is in your map function. The callback arrow function has block body with brackets and so you need to explicitly return your cloned element with the return keyword.
In a concise body, only an expression is needed, and an implicit
return is attached. In a block body, you must use an explicit return
statement.
const childrenWithProps = React.Children.map(this.props.children, child => {
return React.cloneElement(child, {
details: this.state.details
});
});
I am trying to implement React-Router into an existing react app.
How can I use react-router to display components based on some conditions.
var displayRHS;
if(this.state.displayEventComponent){
{/*
* Events Menus
*/}
displayRHS = <DisplayEventComponent
parentFunc={this.displayComponentFunction}
parentPropDay={this.state.day}
/>
} else if (this.state.displayToDoListComponent){
{/*
* To Do List Menu
*/}
displayRHS = <DisplayToDoListComponent
parentCallback_2={this.updateDisplayToDoListComponent}
updateList={this.state.updateDisplayToDoListComponent}
displayIssuesNotList={false}
/>
} else if (this.state.displayIssuesComponent) {
{/*
* Issues menu
*/}
displayRHS = <DisplayIssuesComponent
parentCallback_2={this.updateDisplayToDoListComponent}
updateList={this.state.updateDisplayToDoListComponent}
displayIssuesNotList={true}
/>
}
Displaying Routes breaks
<Route exact path="/" component={displayRHS} />
How can I display these components with their respective props passed in as well?
Many Thanks in advance
PS, I am kind of thinking that a single page should be just that single page and using a routing library should be a sign that you should just have a page refresh instead..
A single page application is called "single page" because the client fetches only one HTML page from the server side. A single page application can have multiple "client-side pages".
The application you are migrating used some condition because it didn't have a router. In react-router, the condition is matching a URL.
The react router allows you to navigate to a client-side page. You will use a component called <Link> to navigate to a client-side page. A virtual page is just a React component. Each available route needs to define a Route. You will need one Route for each client-side page:
<Router>
<Route exact path="/" component={LayoutComponent}>
<IndexRoute component={ToDoPage} />
<Route path="/events" component={EventsPage} />
<Route path="/issues" component={IssuesPage} />
<Route/>
</Router>
The LayoutComponent will always be rendered:
class LayoutComponent extends React.Component {
render() {
return (
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/events">Events</Link></li>
<li><Link to="/issues">Issues</Link></li>
</ul>
{this.props.children}
);
}
}
The value of this.props.children will be the page that matches the URL. So if the URL is /issues the component rendered in {this.props.children} will be the IssuesPage because we configured it that way:
<Route path="/issues" component={IssuesPage} />
Each of your pages can then render the components
ToDoPage:
class ToDoPage extends React.Component {
constructor() {
this.state = {
updateDisplayToDoListComponent: []
};
}
render() {
return (
<DisplayToDoListComponent
parentCallback_2={this.updateDisplayToDoListComponent}
updateList={this.state.updateDisplayToDoListComponent}
displayIssuesNotList={false} />
);
}
public updateDisplayToDoListComponent() {
// do something
}
}
EventsPage:
class EventsPage extends React.Component {
constructor() {
this.state = {
day: 1
};
}
render() {
return (
<DisplayEventComponent
parentFunc={this.displayComponentFunction}
parentPropDay={this.state.day} />
);
}
public displayComponentFunction() {
// do something
}
}
IssuesPage:
class IssuesPage extends React.Component {
constructor() {
this.state = {
updateDisplayToDoListComponent: []
};
}
render() {
return (
<DisplayIssuesComponent
parentCallback_2={this.updateDisplayToDoListComponent}
updateList={this.state.updateDisplayToDoListComponent}
displayIssuesNotList={true} />
);
}
public updateDisplayToDoListComponent() {
// do something
}
}
This is not going to work out of the box and you are going to need to do some reading of the react-router docs but you should have enough details to figure out how to get it to work. You can also lear from "Getting Started with React Router".