This question already has answers here:
Call child method from parent
(23 answers)
Closed 5 years ago.
I have a Meteor+React application which I'm developing where I want to implement the login/registration functionality in a modal. I'm not sure how to open the modal from clicking my sign up or log in buttons
I have the following two components:
ui/components/main-layout/header/LoggedOutNav.jsx
import React, { Component } from 'react'
export default class LoggedOutNav extends Component {
render() {
return(
<ul className="nav navbar-nav">
<li>
<a href="#">
<i className="fa fa-sign-in" aria-hidden="true"></i>
Log In
</a>
</li>
<li>
<a href="#loginRegistration">
<i className="fa fa-user-plus" aria-hidden="true"></i>
Sign Up
</a>
</li>
</ul>
)
}
}
ui/components/modals/LoginRegistration.jsx
import React, { Component } from 'react'
import { Modal } from 'react-bootstrap'
export default class LoginRegistration extends Component {
getInitialState() {
return { showModal: false }
}
close() {
this.setState({ showModal: false })
}
open() {
this.setState({showModal: true})
}
render() {
return (
<Modal show={this.state.showModal} onHide={this.close}>
{/* Irrelevant modal code here */}
</Modal>
)
}
}
How could I accomplish opening the modal from my other component?
import React, { Component } from 'react'
import { Modal } from 'react-bootstrap'
export default class LoggedOutNav extends Component {
constructor(){
this.state = {
showModal: false,
activeModal: ''
}
this.modalDisplay = this.modalDisplay.bind(this);
}
modalDisplay(e){
this.setState({
showModal: !this.state.showModal,
activeModal: e.target.getAttribute('data-tab')
});
}
render() {
return(
<div>
<ul className="nav navbar-nav">
<li
onClick={ this.showModal }
data-tab = 'login'>
<a href="#">
<i className="fa fa-sign-in" aria-hidden="true"></i>
Log In
</a>
</li>
<li
onClick={ this.showModal }
data-tab = 'signup'>
<a href="#loginRegistration">
<i className="fa fa-user-plus" aria-hidden="true"></i>
Sign Up
</a>
</li>
</ul>
<div>
{
this.state.showModal
&&
<Modal modalType={ this.state.activeModal } onHide={this.modalDisplay} data-tab= ""/>
}
</div>
</div>
)
}
}
You could pass the modal type into the Modal component, or use a ternary operator to render
{
this.state.activeModal === 'login' ?
<Login /> :
<SignUp />
}
Related
I am writing a todo-list app and encounter this problem:
If I put the Todo component inside the TodoList component and the todo item is passed as a prop
todo = {name: "ssss", status: false, id: 0.028982865008862824}
the todo.name will be undefined if I need to access it inside the Todo component
// TodoList.js
import React from "react";
export default function TodoList({ todos }) {
function Todo({ todo }) {
console.log(todo); // will print todo as an object
// {name: "xxxx", id: "12334", status: false}
console.log(todo.name); // undefined
return (
<div className="todo">
<li className="todo-item"></li>
<button className="complete-button">
<i className="fas fa-check"></i>
</button>
<button className="trash-button">
<i className="fas fa-trash"></i>
</button>
</div>
);
}
return (
<div className="todo-container">
<ul className="todo-list">
{todos.map((todo) => (
<Todo key={todo.id} todo={todo}></Todo>
))}
</ul>
</div>
);
}
But if I changed this to seperate components as two seperate components and pass the todo item as a prop, it will work. Why is this happening?
// TodoList.js
import React from "react";
import Todo from "./Todo";
export default function TodoList({ todos }) {
return (
<div className="todo-container">
<ul className="todo-list">
{todos.map((todo) => (
<Todo key={todo.id} todo={todo}></Todo>
))}
</ul>
</div>
);
}
// Todo.js
import React from "react";
export default function Todo({ todo }) {
console.log(todo);
console.log(todo.name); // will print the name
return (
<div className="todo">
<li className="todo-item"></li>
<button className="complete-button">
<i className="fas fa-check"></i>
</button>
<button className="trash-button">
<i className="fas fa-trash"></i>
</button>
</div>
);
}
TL;DR You cannot define components inside another component.
I changed this to separate components and it works
That is because that is the correct way to do it! You cannot define your components inside the functions of another component. You can however do a function similar to this:
const renderTodo = (todo) => {
return (
<div className="todo">
<li className="todo-item"></li>
<button className="complete-button">
<i className="fas fa-check"></i>
</button>
<button className="trash-button">
<i className="fas fa-trash"></i>
</button>
</div>
);
}
return (
<div className="todo-container">
<ul className="todo-list">
{todos.map((todo) => (
{ renderTodo(todo) }
))}
</ul>
</div>
);
But I would still advise against it. React is the most useful when split into components, as you did in your 2nd example, like this:
Todo.js
export default ({ todo }) => <p>{todo.name}</p>
TodoList.js
import Todo from './Todo.js'
export default ({ todos }) => <div>{todos.map((todo) => <Todo todo={todo} />}</div>
The reason 1st example is not working is because your Component, which you've defined inside another component is constantly re-rendering, and thus you lose your todo props, making todo.name yield undefined.
I want to toggle between components for the "body" of my app.
trying to make an app of my website.
i have tried many ways (except react-router, or Gatsby) and failed miserably. I am at a point where do not get error message, but nothing other than my state renders.
I want to toggle the "body" from the header button, having the router.js handle/render the different components.
app.js
import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.bundle.min';
import Header from './Components/header.js';
import Router from './Components/router.js';
import Footer from './Components/footer.js';
class App extends React.Component {
render() {
return (
<div className="App">
<Header />
<Router />
<Footer />
</div>
);
}
}
export default App;
router.js
import React from 'react';
import Header, { onClick } from './header.js';
import Main from './main.js';
import About from './about.js';
class Router extends React.Component {
constructor(props) {
super(props);
this.state = { page: <Main /> };
this.changePage = this.changePage.bind(this);
}
changePage(newPage) {
this.setState({
page: newPage
});
}
render() {
return (
<section className="Router">
<div>{this.state.page} <a onClick={this.changePage}></a></div>
</section>
);
}
}
export default Router;
header.js
import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.bundle.min';
class Header extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
const page = e.target.value;
return ({ page: (e) });
}
render() {
return(
<div className="Header">
<nav className="navbar navbar-expand-lg navbar-light" style={{backgroundColor: '#504C5E', color: '#A89B34'}}>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon color-black"></span>
</button>
<div className="col-12 collapse navbar-collapse my-2" id="navbarSupportedContent">
<ul className="navbar-nav col-10">
<li className="nav-item active col-2">
<button onClick={this.handleChange} value="main">Home<span className="sr-only">(current)</span></button>
</li>
<li className="nav-item col-2">
<button onClick={this.handleChange} value="<About />">About Me</button>
</li>
<li className="nav-item col-2">
<button>Education</button>
</li>
<li className="nav-item col-2">
<button>Experience</button>
</li>
<li className="nav-item col-2">
<button>Projects</button>
</li>
</ul>
</div>
</nav>
</div>
);
}
}
export default Header;
I want to display a modular window by clicking the Login or Register button, and I get the following error (TypeError: _this.props.dispatch is not a function). The same code in one project usually works in another.
import React from "react";
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { openModal } from "../modal/index";
import Login from "../auth/Login";
import Register from "../auth/Register";
class Navbar extends React.Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
};
constructor(props) {
super(props);
this.login = this.login.bind(this);
this.register = this.register.bind(this);
}
login() {
this.props.dispatch(
openModal({content: <Login />,title: "Login"}));
}
register() {
this.props.dispatch(
openModal({content: <Register />,title: "Register"}));
}
render() {
const guestLinks = (
<ul className="navbar-nav ml-auto">
<li className="nav-item">
<button
className="m-1 btn-sm btn btn-outline-primary mt-1"
onClick={this.register}>Register</button>
</li>
<li className="nav-item">
<button
className="m-1 btn-sm btn btn-outline-primary mt-1"
onClick={this.login}>Login</button>
</li>
</ul>
);
return (
<nav className="navbar navbar-expand-sm navbar-light bg-light mb-4">
<div className="container">
<Link className="navbar-brand" to="/">
Domras
</Link>
<button
className="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#mobile-nav">
<span className="navbar-toggler-icon" />
</button>
<div className="collapse navbar-collapse" id="mobile-nav">
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<Link className="nav-link" to="/profiles">
Driver
</Link>
</li>
</ul>
</div>
</div>
</nav>
);
}
}
const mapStateToProps = state => ({
auth: state.auth,
});
export default connect(mapStateToProps)(Navbar);
Welcome to Stack Overflow.
If everything is correct, you shouldn't get a problem.
The error message suggests that the dispatch property is something other than a function - if so you should be getting a warning in the console.
A tip to save you some code... use fat arrow functions as below, these are automatically bound to this, so you don't need to bind them in your constructor:
constructor(props) {
super(props);
// No longer needed
// this.login = this.login.bind(this);
// this.register = this.register.bind(this);
}
login = () => {
this.props.dispatch(
openModal({content: <Login />,title: "Login"}));
}
register = () => {
this.props.dispatch(
openModal({content: <Register />,title: "Register"}));
}
I have created a simple app component which renders a side bar and a dashboard. On a link click within the sidebar, I want to do an AJAX request and change the state of the dashboard. I have moved the click handler function to the index.js app component so it can pass the props down to the dashboard.
index.js:
import React from "react";
import { NavBar } from "./components/NavBar";
import { NavBarSide} from "./components/NavBarSide";
import { Dashboard} from "./components/Dashboard"
import { render } from "react-dom";
class App extends React.Component {
handleNavClick(url) {
console.log(url)
}
render() {
return (
<div>
<NavBar/>
<div className="container-fluid">
<div className="row">
<Dashboard/>
<NavBarSide clickHandler={(url) => this.handleNavClick(url)}/>
</div>
</div>
</div>
)
}
}
render(<App/>, window.document.getElementById("root"));
My NavBarSide is like so...
NavBarSide.js:
import React from 'react';
import { Nav, NavItem, NavLink } from 'reactstrap';
export class NavBarSide extends React.Component {
render() {
return (
<Nav className="col-md-2 d-none d-md-block bg-light sidebar">
<div className="sidebar-sticky">
<ul className="nav flex-column">
<NavItem className="nav-item">
<NavLink className="nav-link" href="#" onClick={this.props.clickHandler("/api/highest/price")}>Highest Price</NavLink>
</NavItem>
</ul>
</div>
</Nav>
);
}
}
Instead of the expected behaviour, this function appears to immediately execute.
If there is a better way of doing this (I think with react-router v4) it would be helpful if that was also included.
export default class NavBarSide extends React.Component {
constructor(props) {
super(props);
// Bind the function only once - on creation
this.onClick = this.onClick.bind(this);
}
onClick() {
this.props.clickHandler("/api/highest/price");
}
render() {
return (
<Nav className="col-md-2 d-none d-md-block bg-light sidebar">
<div className="sidebar-sticky">
<ul className="nav flex-column">
<NavItem className="nav-item">
<NavLink className="nav-link" href="#" onClick={this.onClick}>Highest Price</NavLink>
</NavItem>
</ul>
</div>
</Nav>
);
}
}
You need to place the function inside another.
Like this:
onClick={()=>this.props.clickHandler("/api/highest/price")}
If not the render will execute the funcion on mount because you are trigering with the "(...)"
Here is my code:
import React, { Component } from 'react';
import { Link } from 'react-router'
export default class Header extends Component {
render() {
return (
<h1 className="title"><img src="back.png" className="pull-left"/>{this.props.somefield*}</h1>
<ul className="main_menu">
<li><Link to="/" className="active"><i className="fa fa-tachometer" aria-hidden="true"></i><br/>Dashboard</Link></li>
<li><Link to="/favorite"><i className="fa fa-heart" aria-hidden="true"></i><br/>Favorite</Link></li>
<li><Link to="/find"><i className="fa fa-search" aria-hidden="true"></i><br/>Find</Link></li>
<li><Link to="/settings"><i className="fa fa-cog" aria-hidden="true"></i><br/>Settings</Link></li>
</ul>
)}
}
}
The above field is used as a component and my component was used:
<Header *and Some fields/>
How do I use this in the correct way?
Props can be passed as attributes:
<Header someField='Foo' />
//...
render() {
return (
<h1 className="title">
<img src="back.png" className="pull-left"/>
{this.props.someFeild}
</h1>
)
}
Add span or div wrap beginning of component
export default class Header extends Component {
render() {
return (
<span>
<h1 className="title"><img src="back.png" className="pull-left"/> {this.props.name}</h1>
<ul className="main_menu">
<li><Link to="/" className="active"><i className="fa fa-tachometer" aria-hidden="true"></i><br/>Dashboard</Link></li>
<li><Link to="/favorite"><i className="fa fa-heart" aria-hidden="true"></i><br/>Favorite</Link></li>
<li><Link to="/find"><i className="fa fa-search" aria-hidden="true"></i><br/>Find</Link></li>
<li><Link to="/settings"><i className="fa fa-cog" aria-hidden="true"></i><br/>Settings</Link></li>
</ul>
</span>
);
}
}
Then App will be worked ..