import React, {Component} from 'react';
import { Link } from 'react-router';
class AsideNav extends Component{
constructor(props, context) {
super(props, context);
this.state = {
navigation: ''
};
}
componentWillMount(){
console.log(this.props.data.navigation); // data from parent received
this.setState({
navigation: this.props.data.navigation.map(function(el, index){
return(
<Link key={index} className={"aside-nav__link " + (el.modifier ? ("aside-nav__link" + el.modifier) : '')} to={el.url}>{el.name}</Link>
)
})
})
}
render() {
console.log(this.state.navigation); // JSX for navigation is ready
return (
<nav className="aside-nav">
<span className="aside-nav__title" >Категории</span>
{this.state.navigation}
</nav>
);
}
}
export default AsideNav
After (seemingly) successful implementation of code it displays me an empty block and
Uncaught TypeError: Cannot read property 'navigation' of undefined at AsideNav.componentWillMount...
Changes:
1. Never store the ui items in state variable, always store data only and then generate the ui elements dynamically.
2. Use componentWillReceiveProps method, it will get called if you do any changes in props values in parent component, update the state value of child component at that time.
3. Since you just want to create the items from props data, directly use props, instead of storing anything.
Write it like this:
import React, {Component} from 'react';
import { Link } from 'react-router';
class AsideNav extends Component{
constructor(props, context) {
super(props, context);
this.state = { };
}
renderLinks(){
let data = this.props.data;
return data && Array.isArray(data.navigation) && data.navigation.map((el, index) => {
return(
<Link
key={index}
className={"aside-nav__link " + (el.modifier ? ("aside-nav__link" + el.modifier) : '')}
to={el.url}
>
{el.name}
</Link>
)
})
}
render() {
console.log(this.props.data)
return (
<nav className="aside-nav">
<span className="aside-nav__title" >Категории</span>
{this.renderLinks()}
</nav>
);
}
}
se componentWillReceiveProps(newProps) to get props second time as constructor and componentDidMount methods get executed only once. And also add check if data available or not.
import React, {Component} from 'react';
import { Link } from 'react-router';
class AsideNav extends Component{
constructor(props, context) {
super(props, context);
this.state = {
navigation: ''
};
}
componentWillReceiveProps(newProps)
{
console.log(newProps.data.navigation); // data from parent received
this.setState({
navigation: newProps.data &&newProps.data.navigation.map(function(el, index){
return(
<Link key={index} className={"aside-nav__link " + (el.modifier ? ("aside-nav__link" + el.modifier) : '')} to={el.url}>{el.name}</Link>
)
})
})
}
componentWillMount(){
console.log(this.props.data.navigation); // data from parent received
this.setState({
navigation: this.props.data && this.props.data.navigation.map(function(el, index){
return(
<Link key={index} className={"aside-nav__link " + (el.modifier ? ("aside-nav__link" + el.modifier) : '')} to={el.url}>{el.name}</Link>
)
})
})
}
render() {
console.log(this.state.navigation); // JSX for navigation is ready
return (
<nav className="aside-nav">
<span className="aside-nav__title" >Категории</span>
{this.state.navigation}
</nav>
);
}
}
export default AsideNav
import React from 'react';
import {render} from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';
import App from './app';
import PageDashboard from './page-templates/page-dashboard';
import PageRating from './page-templates/page-rating';
import './styles/css/style.css';
//render(<PageDashboard />, document.querySelector('.main-wrapper')); - before
render(
<Router history={browserHistory}>
//<Route path="/public/" component={App} /> - before
<Route path="/public/" component={PageDashboard} /> // - now
<Route path="/public/rating" component={PageRating} />
</Router>, document.querySelector('.main-wrapper')
);
A problem located in root file 'main.js'. I accidentally have mounted two components with common children.
Related
I am trying to redirect to another router when the user clicks a specified button (named Navigate in this case) in a class-based component (named Counter). I am using <Navigate /> for that. It doesn't redirect to the page I specified to redirect to (path='/' for the homepage). In addition, I don't get any errors. Someone, please tell me the best way to use the <Navigate />.
For reference, the code is:
import React, {Component} from "react";
import {Navigate} from 'react-router-dom'
class Counter extends Component {
constructor(props){
super(props)
this.state = {
}
this.navigate = this.navigate.bind(this)
}
navigate(e){
return <Navigate to="/" />
}
render(){
return (
<div className='text-center'>
<h1>Hello there, this is a counter app</h1>
<button onClick={this.navigate} >Navigate</button>
</div>
)
}
}
export default Counter
You should try this. In your component navigate function cannot return Navigation. There are other methods for switching routes programmatically. Read this https://reactrouter.com/docs/en/v6/getting-started/concepts
import React, {Component} from "react";
import {Navigate} from 'react-router-dom'
class Counter extends Component {
constructor(props){
super(props)
this.state = {
dashboard: false,
}
this.navigate = this.navigate.bind(this)
}
navigate(e){
this.setState({dashboard:true})
}
render(){
return (
<div className='text-center'>
{this.state.dashboard && (
<Navigate to="/dashboard" replace={true} />
)}
<h1>Hello there, this is a counter app {this.state.dashboard && <span>dashboard</span>}</h1>
<button onClick={this.navigate} >Navigate</button>
</div>
)
}
}
export default Counter
So I am building a React app and got I think a decent idea pf whay I am doing. But I am looking to find how I can switch between components. Each component is its own individual js file.
App.js file:
import React from 'react';
import './App.css';
import MainPage from './mainpage'
function App() {
return (
<div className="App">
<h1>Welcome to Comix Nation </h1>
<MainPage />
</div>
);
}
export default App;
mainpage.js file:
import React from 'react';
import './App.css';
import CreateAccount from './createaccount.js'
import LogIn from './login.js'
import MainMenu from './mainmenu.js'
class MainPage extends React.Component {
constructor(props){
super(props);
this.state = {
currentPage: 'login'
};
}
getPage(currentPage){
const page ={
mainmenu: <MainMenu />,
createaccount: <CreateAccount />,
login: <LogIn />
};
return page[currentPage]
}
switchPage(currentPage){
this.setState({currentPage});
};
render(){
return (
<div>
<div>
<MainMenu switchPages={this.switchPage}/>
</div>
</div>
);
}
}
export default MainPage;
mainmenu.js file:
import React from 'react';
import './App.css';
class MainMenu extends React.Component {
constructor(props){
super(props);
this.state = {page: 'none'}
}
handleSelection(pageSelection){
this.props.switchPage(pageSelection);
}
render(){
return (
<div>
<h2 onClick={()=> this.handleSelection('createaccount')}>Click to create new account</h2>
<h2>Click to log in</h2>
</div>
);
}
}
export default MainMenu;
The idea is that I can click on either the create or login and get the appropriate js file to render.
so, from reading your code it sounds like you want to do routing (judging from your naming convention at least). There are a number of routing libraries you can use to render different pages if you want to use that. If you just want to switch out components, you've almost got it
class MainPage extends React.Component {
constructor(props){
super(props);
this.state = {
currentPage: 'login'
};
}
switchPage(currentPage){
this.setState({currentPage});
};
render(){
return (
<div>
<div>
{
this.state.currentPage === 'login' &&
<Login/>
}
{
this.state.currentPage === 'MainMenu' &&
<MainMenu/>
}
{
this.state.currentPage === 'SignUp' &&
<SignUp/>
}
</div>
</div>
);
}
}
The way react reads this is true and render this component some people prefer to use a ternary and return null but this is cooler imho 😎
There are several ways to do this, if you are trying to avoid react-router-dom you can implement this system fairly easy.
this.state = {
currentComponent: "",
}
this will allow you to keep track of what component is suppose to show. Put this in your controllers state.
showComponent = (component) => {
this.setState({currentComponent: component})
}
Put this in your main controller file, where you import your components that you will use.
Then you set up your components to display depending what is sent in.
let checkCurrentComponent = this.state.currentComponent;
Make a variable to check for easy checking.
{checkCurrentComponent === "topicList" ? (
<TopicTitles
showComponent={this.showComponent}
/>
) : checkCurrentComponent === "author" ? (
<TopicData
showComponent={this.showComponent}
/>
) : checkCurrentComponent === "commentForm" ? (
<CommentForm }
showComponent={this.showComponent}
/>
): null}
Then in your components you can use that function to pass in the name. Here is how I like to do that.
const handleCommentForm = (e, component) => {
e.preventDefault();
props.showComponent(component);
}
This will be at the top of my stateless function.
will bring up my comment form.
Then the button..
<button
className="btn btn-outline-none"
onClick={e => handleCommentForm(e, "commentForm")}
>
Add Comment
</button>
In Switch router scenario,how do we fetch fresh data from the server, after coming back to a already mounted component which doesn't have any props. I went through many posts before asking this. All posts suggest use componentWillRecieveUpdate. This life cycle hook will never be called if the component doesn't have any props. I even tried the getDerivedStatefromProps
What is best option to deal with scenarios like this.?
import React from "react";
import { render } from "react-dom";
// import react router
import { BrowserRouter as Router, Route, Switch, Link } from "react-router-dom";
class Application extends React.Component {
render() {
return (
<Router>
<div>
<Menu />
<div>
<Switch>
<Route path="/help" component={Help} />
<Route exact path="/" component={OverView} />
<Route component={OverView} />
</Switch>
</div>
</div>
</Router>
);
}
}
class Menu extends React.Component {
render() {
return (
<ul>
<li>
<Link to="/help">Help</Link>
</li>
<li>
<Link to="/">OverView</Link>
</li>
</ul>
);
}
}
class Help extends React.Component {
render() {
return (
<div>
<p>Some help</p>
</div>
);
}
}
class OverView extends React.Component {
constructor(props, context) {
super(props);
this.state = {};
}
getDerivedStatefromProps(prevprops,prevstate, ){
//Even this will also not be called
}
componentWillRecieveUpdate(newprops)
{
//I dont recieve a call to this when come back from help
}
render() {
//can we fetch data here and save in the state
// and re-render. Will this cause any issues
return <div> How to return fetch data</div>;
}
}
render(<Application />, document.getElementById("root"));
I have been digging in your code a little bit, please have a notice that your component is mounting every time, it's not one time mounted as you claimed.
please read the React Router doc to understand more.
You can see it in the Demo
class OverView extends React.Component{
constructor(props,context) {
super(props)
this.state = {}
}
componentDidMount() {
console.log('mounted');
}
render() {
console.log('render');
return(<div> How to return fetch data</div>)
}
}
Issue
I have a problem with conditional rendering of a component. As far as I can see, there are 2 approaches to doing this. First approach is ugly as it becomes difficult when I have to do multiple && conditions. The second way is clear, but it adds the component itself to the state and further computations with the state value is difficult. E.g checking what is the message value for error.
I have given both the approaches below. Please let me know which would be better. Is there a another approach than both of them?
Application
This is a simple application that renders either 'Main' component or 'Err' component, based on the state of 'err' attribute in first approach and content of the comp attribute in second approach.
Initially Main component is rendered. The err attribute is updated to some value after 2 seconds, which triggers rerendering. At this time, I want Err component to render.
The real application is I have an external api call on componentDidMount and it can either fail or succeed. I have to display different components based on result. It is a little more complicated with multiple state values being updated. I have simplified the issue below for the purpose of demonstration.
Common steps for both types
npx create-react-app react-oop
component/Err.js
import React,{Component} from 'react'
class Err extends Component {
render(){
return(
<div>
Error Component
</div>
)
}
}
export default Err
component/Main.js
import React, {Component} from 'react'
class Main extends Component {
render(){
return(
<div>
Main Component
</div>
)
}
}
export default Main
First approach
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Err from './components/Err'
import Main from './components/Main'
class App extends Component {
constructor(props){
super(props)
this.state = {
err: null
}
this.setError = this.setError.bind(this)
}
setError(){
return(
this.setState(() => {
return({
err: 'Error'
})
})
)
}
componentDidMount(){
setTimeout(this.setError, 2000)
}
render() {
return (
<div className="App">
{
this.state.err ? <Err /> : <Main />
}
</div>
);
}
}
export default App;
Second approach
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Err from './components/Err'
import Main from './components/Main'
class App extends Component {
constructor(props){
super(props)
this.state = {
comp: <Main />
}
this.setError = this.setError.bind(this)
}
setError(){
return(
this.setState(() => {
return({
comp: <Err />
})
})
)
}
componentDidMount(){
setTimeout(this.setError, 2000)
}
render() {
return (
<div className="App">
{this.state.comp}
</div>
);
}
}
export default App;
I definitely recommend the 1st approach. Store data (json), not views (jsx) in your component's state.
Actually there is a 3rd approach that takes the best of both:
use a jsx variable to edit the view (with your logic) before rendering
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Err from './components/Err';
import Main from './components/Main';
class App extends Component {
constructor(props){
super(props);
this.state = {
err: null
};
}
// This way of writing functions saves you the binding
setError = () => this.setState({err: 'Error'})
componentDidMount(){
setTimeout(this.setError, 2000);
}
render() {
let comp = <Main />;
// Put your logic here so your returned JSX is clear
if (this.state.err)
comp = <Err />;
return (
<div className="App">
{comp}
</div>
);
}
}
export default App;
Both approaches are essentially the same but I prefer option 1 as it's simpler to grasp. You can also use something like babel-plugin-jsx-control-statements#choose which makes the React component look simpler:
<Choose>
<When condition={ test1 }>
<Main />
</When>
<When condition={ test2 }>
<AnotherMain />
</When>
<Otherwise>
<Err />
</Otherwise>
</Choose>
I'm getting an error stating that setstate can only be update on mounted or mounting component. I actually want to toggle display of some content. Please help me out. This is the code.
I want to toggle the boolean "show" to true or false. if true, it should show Todolist, if false, it should not show Todolist, instead it should show Footer. Pardon me for framing the question badly. But Please tell me what to do and how to do. Thank you!
import React, {Component} from "react";
import ReactDOM from "react-dom";
import "./index.css";
import TodoList from "./TodoList";
import NavbarTodo from "./NavbarTodo";
import Footer from "./Footer";
import "bootstrap/dist/css/bootstrap.css";
var destination = document.querySelector("#container");
var navdest = document.querySelector(".putnavbarhere");
var clicked=false;
class index extends Component{
constructor() {
super();
this.state = {show: true};
this.handleAbout = this.handleAbout.bind(this);
}
handleAbout(){
this.setState({
show:false
})
clicked=!clicked
if(clicked){
ReactDOM.render(
<Footer />,
document.querySelector("#toast")
)
}
else{
ReactDOM.render(
<p> </p>,
document.querySelector("#toast")
)
};
}
}
var indObj = new index();
ReactDOM.render(
<div>
<TodoList show={indObj.state.show}/>
</div>,
destination
);
ReactDOM.render(
<NavbarTodo clicked={indObj.handleAbout}/>,
navdest
);
What you need is some conditional rendering.
https://reactjs.org/docs/conditional-rendering.html
Your onClick function will toggle this.state.show between true/false.
class Index extends Component {
constructor () {
super()
this.state = {show: true}
}
handleClick () {
this.setState({show: !this.state.show})
}
render () {
return (
<div>
{this.state.show &&
<Todolist component here>
}
{!this.state.show &&
<Footer component here>
}
</div>
)
}
}