Pass OnClick event from component to other component - javascript

Lets say there is 2 component header.js and layout.js
// header.js
import React from 'react';
class Header extends React.Component {
state = { showMenu: false }
toggleMenu = () => {
this.setState({
showMenu: !this.state.showMenu
})
}
render() {
const menuActive = this.state.showMenu ? 'active' : '';
const buttonActive = this.state.showMenu ? 'active' : '';
return (
<button className={`button ${buttonActive}`} onClick={this.toggleMenu}>
Some Button
</button>
<div className={`menu ${menuActive}`}>
...
</div>
)
}
}
export default Header
// layout.js
import React from 'react';
import Header from './header';
const Layout = (props) => {
<div className="foo"> // <==== need to add class from onClick event in header.js
<Header/>
<main>
{props.children}
</main>
</div>
}
export default Layout
motivation
As you can see I have managed to create onClick event in the header.js but in the other hand i also need to pass the event to layout.js so i can add some additional class. Maybe this can be done using props, unfortunately since i new in React i have no idea how to implement it into the code

I have a simple solution.
You can declare the state and function on the parent component.
in your code, you just declare them on Layout.
header.js
import React from 'react';
class Header extends React.Component {
/* state = { showMenu: false }
toggleMenu = () => {
this.setState({
showMenu: !this.state.showMenu
})
}*/
render() {
// const menuActive = this.state.showMenu ? 'active' : '';
// const buttonActive = this.state.showMenu ? 'active' : '';
const menuActive = this.props.showMenu ? 'active' : '';
const buttonActive = this.props.showMenu ? 'active' : '';
return (
<button className={`button ${buttonActive}`} onClick={this.props.toggle}>
Some Button
</button>
<div className={`menu ${menuActive}`}>
...
</div>
)
}
}
export default Header
layout.js
import React from 'react';
import Header from './header';
const Layout = (props) => {
const [showMenu, setShowMenu] = React.useState(false);
const toggle = () => setShowMenu(!showMenu);
return(
<div className={`foo ${showMenu? 'active':''}`}> // <==== need to add class from onClick event in header.js
<Header showMenu={showMenu} toggle={toggle}/>
<main>
{props.children}
</main>
</div>
)
}
export default Layout
And, there're some other options
i.e Redux, Mobx...
If you are going to build a complicated and huge application, you should use one of them.

You should control the state in the Layout component, and pass the toggleMenu callback as a prop to the Header component:
// header.js
import React from 'react';
const Header = ({ menuActive, toggleMenu}) => {
return (
<button className={`button ${menuActive}`} onClick={toggleMenu}>
Some Button
</button>
<div className={`menu ${menuActive}`}>
...
</div>
)
}
export default Header
// layout.js
import React from 'react';
import Header from './header';
class Layout extends React.Component {
state = { showMenu: false }
toggleMenu = () => {
this.setState({
showMenu: !this.state.showMenu
})
}
render() {
const menuActive = this.state.showMenu ? 'active' : '';
return (
<div className={`foo ${menuActive}`}>
<Header menuActive={menuActive} toggleMenu={this.toggleMenu} />
<main>
{this.props.children}
</main>
</div>
)
}
}
export default Layout

Related

Text area not rendering React

I'm not sure why my text area isn't rendering if someone could suggest what I'm missing or what I've done wrong I'd be forever grateful.
I'm not sure whether maybe I should try rending it in the App.js file.
I'm also sure I don't need to import the App.js file to the CardCheck.js because it's a child.
import "./App.css";
import React from "react";
import CardCheck from "./CardCheck";
class App extends React.Component() {
state = {
cardNumber: "",
};
handleChange = (event) => {
this.setState({ cardNumber: event.target.value });
};
handleClick = () => {
const { cardNumber } = this.state;
this.setState({
cardNumber: "",
});
};
render() {
const { cardNumber } = this.state;
return (
<div className="App">
<h1>Taken Yo Money</h1>
<CardCheck
cardNumber={cardNumber}
handleChange={this.handleChange}
handleClick={this.handleClick}
/>
</div>
);
}
}
export default App;
function CardCheck(props) {
const { cardNumber, handleChange, handleClick } = props;
return (
<div className="TweetInput">
<div className="bar-wrapper"></div>
<textarea onChange={handleChange} value={cardNumber}></textarea>
<footer>
<button onClick={handleClick}>Enter Card Details</button>
</footer>
</div>
);
}
export default CardCheck;
Remove the parentheses from class App extends React.Component(). It should be
class App extends React.Component {
//rest of the code
}
``
You need to replace class App extends React.Component() with class App extends Component {
The Component is imported from import React, { Component } from "react";
It should fix the rendering issue

Using button in one component to render another component in main (App) component

I am trying to display/hide one component which is ItemMain and which is imported to the main App component using button in another component which is NavLogoNew. I tried to do this in many different ways but it looks like the button doesn't know if it's clicked, when I change true/false manually it works. In web I found a lot of stuff about situations when only two components are involved, but nothing like this. My code:
App
import React from 'react';
import './App.css';
import { tsPropertySignature } from '#babel/types';
import { statement } from '#babel/template';
import NavBar from './../Components/Navigation/NavBar/NavBar.js';
import ItemMain from './../Components/Item/ItemMain/ItemMain.js';
import ItemList from './../Components/Item/ItemList/ItemList.js';
import NavButtonTop from './../Components/Navigation/NavButton/NavButtonTop/NavButtonTop.js';
import NavLogoNew from './../Components/Navigation/NavButton/NavButtonNew/NavLogoNew.js';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
visible: !this.visible
})
}
render() {
return (
<div className="App">
<NavBar />
{this.state.visible ? <ItemMain /> : null}
<ItemList />
<NavButtonTop name='UP'/>
</div>
);
}
}
export default App;
NavLogoNew:
import React from 'react';
import './NavLogoNew.css';
import ItemMain from './../../../Item/ItemMain/ItemMain.js'
class NavLogoNew extends React.Component {
render() {
return (
<button
className='NavLogoNew'
onClick={this.props.click}
>
{this.props.name}
</button>
);
}
}
export default NavLogoNew;
Your handleClick function is lacking something
use !this.state.visible so change from the below
handleClick(){
this.setState({
visible: !this.visible
})
}
to
handleClick = () => {
this.setState({
visible: !this.state.visible
})
}
pass the handleClick function to the NavLogoNew as follows
<NavLogoNew onClick = {this.handleClick} />
inside of the NavLogoNew component you should invoke it as follows
class NavLogoNew extends React.Component {
render() {
return (
<button
className='NavLogoNew'
onClick={() => this.props.onClick()}
>
{this.props.name}
</button>
);
}
}

how can i call react state different js

i want to change state different js but i can not , i have a sidebar.js with react-burger-menu
i want to call and change toggleMenu state in header.js
When I click the menu link, i want to toggle react-burger-menu but different js. this is not working.
sidebar.js
import React from "react";
import PropTypes from "prop-types";
import { reveal as Menu } from "react-burger-menu";
import * as FontAwesome from "react-icons/lib/fa";
export default class SidebarMenu extends React.Component {
constructor (props) {
super(props)
this.state = {
menuOpen: false
}
}
handleStateChange (state) {
this.setState({menuOpen: state.isOpen})
}
closeMenu () {
this.setState({menuOpen: false})
}
toggleMenu () {
this.setState({menuOpen: !this.state.menuOpen})
}
render () {
return (
<div>
<Menu
isOpen={this.state.menuOpen}
onStateChange={(state) => this.handleStateChange(state)}
>
// menu content
</Menu>
</div>
</div>
)
}
}
header.js have link for react-burger-menu
import React from 'react';
import PropTypes from 'prop-types';
import SidebarMenu from "../SidebarMenu";
export default class Header_Video extends React.Component {
render() {
return (
<Container>
<Row>
<Col md={5} sm={12} xs={12} className="text-left mobile-right">
<div className="bar__module">
<a onClick={this.toggleMenu}>Menu</a>
</div>
</Col>
</Row>
</Container>
);
}
}
thanks for help
note: i have a app.js all files import. I want to run toggleMenu in header.js
app.js
const TemplateWrapper = ({ children }) => (
<div id="outer-container">
<SidebarMenu />
<main id="page-wrap" className="page-wrap">
<HeaderVideo /> {children()}
<Footer />
</main>
</div>
);
menuOpen should be in a parent state of both components.
Example:
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
menuOpen: false
}
}
closeMenu = () => {
this.setState({menuOpen: false})
}
toggleMenu = () => {
this.setState({menuOpen: !this.state.menuOpen})
}
render() {
return (
<div>
<SidebarMenu isMenuOpen={this.state.menuOpen} toggleMenu={this.toggleMenu} />
<Header toggleMenu={this.toggleMenu} />
</div>
)
}
}
You may have further errors beyond just this, but the glaring error to me is that toggleMenu() is not bound to the constructor.
https://reactjs.org/docs/handling-events.html
try:
import React from "react";
import PropTypes from "prop-types";
import { reveal as Menu } from "react-burger-menu";
import * as FontAwesome from "react-icons/lib/fa";
export default class SidebarMenu extends React.Component {
constructor (props) {
super(props)
this.state = {
menuOpen: false
}
this.toggleMenu = this.toggleMenu.bind(this);
// the above statement binds the function to the object constructor
}
handleStateChange (state) {
this.setState({menuOpen: state.isOpen})
}
closeMenu () {
this.setState({menuOpen: false})
}
toggleMenu () {
this.setState({menuOpen: !this.state.menuOpen})
}
render () {
return (
<div>
<Menu
isOpen={this.state.menuOpen}
onStateChange={(state) => this.handleStateChange(state)}
>
// menu content
</Menu>
</div>
</div>
)
}
}
You'll also want to use an HTML5 button tag instead of a link tag, the correct HTML semantic structure provides a bunch of underlying features and greatly improves accessibility out of the box.
Also, remove the arrow function and pass a reference to the function, not the returned value. This is so react doesn't call the function immediately but stores the function reference to execute upon the click event.
<button onClick={this.toggleMenu}>Menu</button>
// instead of
<a onClick={() => this.toggleMenu()}>Menu</a>
Hope this helps!

How set the state of parent component when i toggle between the links and fetch the data based on tag value

Task is to fetch data from api when toggle between tags
When click on the link it calls the api service but state of feeds is not updated but it throws below warning
jQuery.Deferred exception: Cannot read property 'setState' of undefined TypeError: Cannot read property 'setState' of undefined
My github repo
https://github.com/dolphine4u/demo-app
APP component
import React from 'react';
import {FetchData} from "../service/flickerApi.service";
import Header from "./header/header.component";
import Navigation from "./navigation/navigation.component";
import ProductList from "./products/products.component";
import Footer from "./footer/footer.component";
class App extends React.Component {
constructor() {
super()
this.state = {
feeds: [],
favorites:[]
};
this.addToFavorites = this.addToFavorites.bind(this);
}
handleChange( value ) {
this.setState( { feeds: value })
}
addToFavorites(id) {
const {feeds ,favorites} = this.state;
const findId = feeds.filter(item => {
return item.id === id;
})
favorites.push(findId)
console.log(favorites)
// localStorage.setItem('favorite', JSON.stringify(this.state.favorites));
this.setState({
feeds: favorites
});
}
/* componentWillMount(){
let LoadFeeds = localStorage.getItem('FlickerFeeds');
LoadFeeds && this.setState({
feeds: JSON.parse(LoadFeeds)
})
}*/
componentDidMount() {
FetchData.call(this);
}
/* componentWillUpdate(nextprops, nextState){
localStorage.setItem('FlickerFeeds', JSON.stringify(nextState.feeds))
}
*/
render() {
const {feeds} = this.state;
const productList = feeds.map((item,index) => {
return <ProductList
key={index}
title={item.title}
image={item.src}
id={item.id}
author={item.author}
date={item.created}
update={this.addToFavorites}
/>
})
return ([
<Header key="header"/>,
<Navigation key="navigation" />,
<section key="productList">
<div className="container">
<div className="row row-eq-height">
{productList}
</div>
</div>
</section>,
<Footer key="footer"/>
]);
}
}
export default App;
Navigation component
import React from 'react';
import Link from "./link.component";
import './navigation.css';
class Navigation extends React.Component {
constructor(props) {
super(props)
this.state = {
tags: [
{tag:"kittens"},
{tag:"dogs"},
{tag:"lion"},
{tag:"tiger"},
{tag:"leapord"}]
};
}
render() {
const {tags} = this.state;
const tagList = tags.map(item => {
return <Link
key={item.tag}
tag={item.tag}
/>
})
return (
<nav className="nav">
<div className="container">
<ul className="nav-bar">
{tagList}
</ul>
</div>
</nav>
);
}
}
export default Navigation;
Link Component
import React from 'react';
import {FetchData} from "../../service/flickerApi.service";
class Link extends React.Component {
constructor(props) {
super(props)
this.onClick = this.onClick.bind(this);
}
onClick(e) {
FetchData(this.props.tag);
}
render() {
return (
<li><a href="#" onClick={this.onClick}>{this.props.tag}</a></li>
);
}
}
export default Link;
product component
import React from 'react';
import './product.css';
class ProductList extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick(e) {
this.props.update(this.props.id);
}
render() {
return (
<div className="product-column">
<div className="product-item">
<div className="product-content">
<div className="product-author">
<strong>Author: </strong>{this.props.author}
</div>
{/*<div className="product-image" style={{backgroundImage: "url(" + this.props.image + ")"}}/>*/}
</div>
<div className="product-content">
<div className="product-date">
Created Date: {this.props.date}
</div>
<h3 className="product-title">{this.props.title}</h3>
<button className="product-btn" onClick={this.onClick}>
Add to Favourites
</button>
</div>
</div>
{/*<div className="product-description" dangerouslySetInnerHTML={{__html: this.props.description}}>
</div>*/}
</div>
);
}
}
export default ProductList;
Api service
import $ from "jquery";
import {getLastPartOfUrl, formatDate, removeUrl, getString} from "../helpers/helper";
export function FetchData(tag) {
const URL = "https://api.flickr.com/services/feeds/photos_public.gne?format=json&jsoncallback=?"
const SUFFIX_SMALL_240 = "_m";
const SUFFIX_SMALL_320 = "_n";
$.getJSON({
url : URL,
data: {
tags: tag
}
})
.then(response => {
let list= response.items.map(item => ({
title: removeUrl(item.title),
id: getLastPartOfUrl(item.link),
description: item.description,
link: item.link,
src: item.media.m.replace(SUFFIX_SMALL_240, SUFFIX_SMALL_320),
author: getString(item.author),
created: formatDate(item.published),
tags: item.tags,
fav: false
}));
this.setState({
feeds: list
})
}).catch(function(error){
console.log(error);
});
}
You're trying to call this.addToFavorites from a click handler that is not even bound to this. I think two changes are needed for this to work:
In App component, change the addFavorites function to an arrow function so it gets the context this:
addToFavorites = id => {
...
Same in ProductList component for the click handler:
onClick = () => {
this.props.update(this.props.id);
}

How can I test React component's style with Jest + Enzyme?

Alright I have a component called <TestButton />. Inside the <TestButton /> there are two Semantic UI React component, <Button /> and <Header>.
Basically, when the <Button> is clicked, it toggles display: none; to <Header>.
I want to check (I want to learn) on how to assert <Header>'s display: none; when <Button> is clicked.
TestButton.js
const TestButton = (props) => {
return (
<div id='test-button'>
<Header id='please-hide-me' size='huge'>Please Hide Me</Header>
<Button
onClick={
() => {
hiding = !hiding;
let findDOM = document.getElementById(searchForThisID);
if (findDOM) { findDOM.style.display = hiding ? 'none' : ''; }
return hiding;
}
}
>
Sample Toggle
</Button>
</div>
);
};
My unit test is based on How to test style for a React component attribute with Enzyme. It looks like this:
test(`
`, () => {
const wrapper = shallow(<TestButton />);
const button = wrapper.find('Button');
const header = wrapper.find('Header');
const headerStyle = header.get(0).style;
expect(headerStyle).to.have.property('display', '');
wrapper.find('Button').simulate('click');
expect(headerStyle).to.have.property('display', 'none');
}
);
But it has this error:
TypeError: Cannot read property 'have' of undefined
What should I do?
There are a few mistakes in your provided code:
You should not be using DOM element's style property because React does not manage it. Shift the hidden property into the state instead.
I believe headerStyle is a shallow copy of the style object. After you simulate click, it does not get updated. You will have to query the element again for the style object.
to.have.property is not valid Jest syntax. It should be toHaveProperty.
Please refer to the corrected code here. If you paste the following into create-react-app, it should just work.
app.js
import React, { Component } from 'react';
function Header(props) {
return <h1 style={props.style}>Header</h1>;
}
function Button(props) {
return <button onClick={props.onClick}>Click Me</button>;
}
export class TestButton extends React.Component {
constructor(props) {
super(props);
this.state = { hiding: false };
}
render() {
return (
<div>
<Header
id="please-hide-me"
style={{
display: this.state.hiding ? 'none' : '',
}}
>
Please Hide Me
</Header>
<Button
onClick={() => {
this.setState({
hiding: !this.state.hiding,
});
}}
>
Sample Toggle
</Button>
</div>
);
}
}
class App extends Component {
render() {
return (
<div className="App">
<TestButton />
</div>
);
}
}
export default App;
app.test.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { configure, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
import { TestButton } from './App';
it('renders without crashing', () => {
const wrapper = shallow(<TestButton />);
expect(wrapper.find('Header').get(0).props.style).toHaveProperty(
'display',
'',
);
wrapper.find('Button').simulate('click');
expect(wrapper.find('Header').get(0).props.style).toHaveProperty(
'display',
'none',
);
});

Categories

Resources