How to switch between Components in React.js - reactjs

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>

Related

Call class method from another component

I'm stuck with React what's a new programming environment for me. So probably I use wrong names for certain objects.
I want to call a method in a class in file 'App.jsx' from a component in file 'Navbar.jsx'. This is (part of) my code so far:
App.jsx:
import React, {} from "react";
import { Navbar, Home, Footer, Documentation } from "./components";
class App extends React.Component {
constructor(props)
{
super(props);
this.state = { mainComponent: 'Home' };
}
getClick = () => {
console.log('test');
}
render() {
return (
<div className="min-h-screen gradient-bg-welcome">
<Navbar getClick={this.getClick}/>
<Home/>
<Footer/>
</div>
);
}
}
export default App;
Navbar.jxs:
...
const Navbar = () => {
...
return (
...
<div ... onclick={() => (call getClick in App.jsx here)}>
...
</div>
);
}
export default Navbar;
I searched the internet and tried some code from several examples, but I probably miss something simple. I can't figure out how to call getClick from Navbar.jsx.
I also tried using states, which should be a better option from what I read, but that also didn't work out for me.
My goal:
In App.jsx I have this:
<Navbar/>
<Home/>
<Footer/>
I want, from the Navbar where I have some link texts, to reload/change the component between Navbar and Footer for another component based on what link I click in the Navbar.
Try this
const Navbar = (props) => {
...
return (
...
<div ... onclick={() => props.getClick()}>
...
</div>
);
}
export default Navbar;
What we did here is simple, we simply passed the function as a prop from the parent. And used the passed props to access and call the function.

Navigate does not redirecting to the specified path in reactjs

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

Why I can't use an imported component inside a functional component in React?

I am new to React. For the code readability, instead of in-line styled button, I want to write it as a separate class component. I created a customed button 'addImageButton'and imported it to another .js file. It doesn't render the customer button when I try to use it within a functional component. How can I make the functional component be able to use the imported button? Thanks!
//addImageButton.js
import React, { Component } from "react";
class addImageButton extends Component {
render() {
return (
<div>
<button
style={{
borderStyle: "dotted",
borderRadius: 1,
}}
>
<span>Add Image</span>
<span>Optional</span>
</button>
</div>
);
}
}
export default addImageButton;
//AddNewTaskButton.js
import React, { Component } from "react";
import Modal from "react-modal";
**import addImageButton from "../addImageButton";**
class AddNewTaskButton extends Component {
constructor(props) {
super(props);
this.state = {
show: false,
};
this.setShow = this.setShow.bind(this);
this.closeShow = this.closeShow.bind(this);
this.addTaskModal = this.addTaskModal.bind(this);
}
setShow() {
this.setState({
show: true,
});
}
closeShow() {
this.setState({
show: false,
});
}
addTaskModal = () => {
return (
<div>
<Modal
isOpen={this.state.show}
onRequestClose={() => this.closeShow()}
>
**<addImageButton />**
</Modal>
</div>
);
};
render() {
return (
<div>
<button onClick={() => this.setShow()}>
<img src={addIcon} alt={text}></img>;
<span>text</span>
</button>
<this.addTaskModal className="modal" />
</div>
);
}
}
export default AddNewTaskButton;
Easier way would be to just use functional components. Also, react components should be upper case, like so:
export default function AddImageButton() {
return (
<div>...</div>
)
}
create a different component for Modal
import Modal from './Modal'
import AddImageButton from './AddImageButton'
function AddTaskModal() {
return (
<div>
<Modal> <AddImageButton/> </Modal>
</div>
)
}
then
import AddTaskModal from './AddTaskModal'
function AddNewTaskButton() {
return (
<div>
<AddTaskModal/>
</div>
)
}
I don't know your file directories, so I just put randomly.
as for your question, try to make the AddImageButton as a class and see if it renders then. If it doesn't it might be due to something else. Do you get errors? Also maybe create the AddTaskModal class separately and render it out as a component. Maybe that'll help

TypeError: robots.map is not a function

I keep getting this error: TypeError: robots.map is not a function.
I reviewed the code several times can't find the bug.
import React from 'react';
import Card from './Card';
// import { robots } from './robots';
const CardList = ({ robots }) => {
return(
<div>
{
robots.map((user, i) => {
return (
<Card
key={i}
id={robots[i].id}
name={robots[i].name}
email={robots[i].email}
/>
);
})
}
</div>
);
}
export default CardList;
App.js
import React, { Component } from 'react';
import CardList from './CardList';
import SearchBox from './SearchBox';
import { robots } from './robots';
class App extends Component {
constructor(){
super()
this.state = {
robots:'robots',
searchfield: ''}
}
render(){
return(
<div className='tc'>
<h1 className=''>RoboFriends</h1>
<SearchBox />
<CardList robots={this.state.robots}/>
</div>
);
}
}
export default App;
I updated the initial code with App.js that calls CardList.
I recently started learning react and I hope to develop an app that lets you search for a user which instantly filters and render the name typed in the search box.
You pass robots as props from App internal state and not from the imported file.
Set the state of App component from the imported robots file
import { robots } from './robots'
class App extends Component {
constructor() {
super()
this.state = {
robots,
searchfield: ''
}
}
render() {
return (
<div className='tc'>
<h1 className=''>RoboFriends</h1>
<SearchBox />
<CardList robots={this.state.robots}/>
</div>
);
}
}
Also using index as React key is a bad practice, You have a unique id in every robot object so use it as key, also read about the map function and how to access the iterated elements
const CardList = ({ robots }) => (
<div>
{robots.map(robot => (
<Card
key={robot.id}
id={robot.id}
name={robot.name}
email={robot.email}
/>
))}
</div>
);
You're passing a string to be mapped, instead pass the robots list of objects and see the result.
These kind of errors are the result of passing something other than a list to be mapped

on click i want to generate alert in react js method

This is my code:
generateAlert = () => {
alert('hi');
}
return <Tile
click={(index)=>{this.generateAlert}}
title={tile.title}
value={tile.value}
key={tile.id}
/>
This is the error I'm getting:
Expected an assignment or function call and instead saw an expression no-unused-expressions
Search for the keywords to learn more about each error.
First, I do wonder if in your Component you have an array of Tile data, and you want to render a Tile for each entry of the array (I thought so because you added the key prop to Tile).
Anyways, I made an example similar to what you want to achieve, and it's working. Look at this:
const Tile = (props) => {
return (
<div className="Tile">
<h3>{props.title}</h3>
<div onClick={props.click}>
{props.value}
</div>
</div>
);
}
class App extends React.Component {
constructor(props) {
super(props);
}
generateAlert = () => {
alert("Hi");
}
render() {
return (
<Tile
click={this.generateAlert}
title={"This isa a Title"}
value={"This is the value"} />
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
#import url(https://fonts.googleapis.com/css?family=Montserrat);
body {
font-family: 'Montserrat', sans-serif;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'></div>
Now, I may help you in a deeper way if you would post the code of the Component that wants to render Tile; maybe, there are some error in that.
Hei!
If it's a function invocation inside your component's onClick function, you need to add () after this.generateAlert in your component
So it's gonna be like:
return <Tile
click={(index)=>{this.generateAlert()}}
title={tile.title}
value={tile.value}
key={tile.id}
/>
Otherwise, you can use your function as a onClick callback per se.
In that case you need to have it like this:
return <Tile
onClick={this.generateAlert}
title={tile.title}
value={tile.value}
key={tile.id}
/>
Cheers!
I will do in this way:
Q: why I export Tile to new component?
A: As each component should be as short as possible. There is a many advantages to doing in this way
like: "easy to find bugs (testing)".
import React, { Component } from "react";
import Tile from "./Tile";
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.generateAlert = this.generateAlert.bind(this);
}
generateAlert = () => {
alert("Hi");
};
render() {
return (
<Tile
click={this.generateAlert}
title={"This isa a Title"}
value={"This is the value"}
/>
);
}
}
export default App;
and file Tile.js:
import React, { Component } from "react";
export default class Tile extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<button onClick={this.props.click}>click me</button>
<p>{this.props.title}</p>
<p>{this.props.value}</p>
</div>
);
}
}
This file Tile.js are ready for future addons but if you want to use only like it is now I would recommend to change into stateless component:
import React from "react";
const Tile = props => {
return (
<div>
<button onClick={props.click}>click me</button>
<p>{props.title}</p>
<p>{props.value}</p>
</div>
);
};
export default Tile;

Resources