I have been facing a problem, I would like to write a div every time a specific button is clicked, but the button is in one component and the menu in which I want the div to be created is in another component as well , and both main components are in one component, is there a way of making this happen? So the structure of it all is something like this:
This is the root component with the smaller components in it:
export class Root extends React.Component {
render() {
return(
<div className="Body">
<div id="SideBar" ><SideMenu/></div>
<MenuCocktails/>
<MenuBeers/>
<MenuVines/>
<MenuLemonades/>
<MenuSoftDrinks/>
<MenuCollection/>
<MenuCharger/>
<div id="BottomMenu"><BottomMenu/></div>
</div>
)
}
}
this is the menu that I wanted to div to be created in:
export class BottomMenu extends React.Component{
constructor() {
super();
this.state = {
shown: ''
};
}
toggleMenu = () => {
this.setState({shown: this.state.shown ? '' : 'visible'});
}
render() {
return(
<div>
<button
id="button"
className={this.state.shown}
onClick={this.toggleMenu}
>
My Cart
</button>
<div id="Header" className={this.state.shown}>
<button id="CheckOutButton">Check Out</button>
</div>
</div>
);
}
}
and this is one of the components with the button:
import React from "react";
import styles from "./MenuCocktails.css";
import plus from "./plus.png";
export class NiğdeGazozu extends React.Component{
render() {
return (
<div id="menu-items-1">
<div id="item-name">NIĞDE GAZOZU</div>
<div id ="price-item">8 TL</div>
<button id="plusbutton"><img src={plus}/></button>
</div>
);
}
}
Related
I have a FileUploader which needs to trigger from outside the component via prop, i have been battling for hours and could not figure out.
export class App {
let button = (<button>Click me please!</button>);
render() {
return (
<div className="app">
{button}
<FileUploader trigger={button} />
</div>
);
}
}
export class FileUploader {
constructor(props) {
this.trigger = props.trigger; // <button>Click me please!</button>
// do something to attach an onclick trigger element to simulate a
// click to the file input
}
render() {
return (
<div className="file-uploader">
<input type="file" style="display: none;">
<div>
// display the file image and some other additional file info
</div>
</div>
);
}
}
i wish that this.trigger will simulate a click to the input, which will open up a window to select files .. how can i do this? much appreciated
You can pass a empty ref to FileUploader from the parent component, and we will assign that ref to the input-file, and when you click on the button you have the access to the input, so you can manually trigger click
export default class App extends React.Component {
constructor(props) {
super(props);
this.fileInput = React.createRef(); // Create a empty ref
}
openFileInput = () => {
this.fileInput.current.click();
// since we have access to the fileinput, we can trigger manual click
};
render() {
return (
<div>
<button type="button" onClick={this.openFileInput}>
open file browser in child component
</button>
.......
<FileInput fileInputRef={this.fileInput} />
// pass the empty ref to the file input so we can assign it to the input-file
</div>
);
}
}
export class FileInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="file-uploader">
....
<input
type="file"
ref={this.props.fileInputRef} // assign the ref to the input
style={{ display: "none" }}
/>
</div>
);
}
}
DEMO
Here is a small example of how you can pass function to child component from parent and call them from child component.
import React from "react";
import ReactDOM from "react-dom";
class Children extends React.Component {
constructor(props) {
super(props);
this.onPress = props.onPress;
}
render = () => {
return (
<div>
<button onClick={this.onPress} color="success">
Child Click
</button>
</div>
);
};
}
class Parent extends React.Component {
fromParent = () => {
alert("Called from children");
};
render() {
return <Children onPress={this.fromParent} />;
}
}
ReactDOM.render(<Parent />, document.getElementById("root"));
I have created a react component and I'm reusing it in other components.
<SC_Button label = "English" btnStyle = "sc-btn-default--sinhala mb-2" onClick={this.handleClick}/>
But function defined at onClick does not execute because it's with props passed to the component. I guess react reads onClick as a prop as well. I'm quite new to react.
Below way works. But I don't want to wrap my react component with an extra div due to a styling issue.
<div onClick={this.handleClick} >
<SC_Button label = "English" btnStyle = "sc-btn-default--sinhala mb-2"/>
</div>
Is there any way to use props along with other attributes in react component definitions ?
Edit :
Parent Component
import React from 'react';
import { withRouter } from "react-router-dom";
import SC_Button from '../components/button';
class Home extends React.Component {
handleClick = () => {
this.props.history.push('/page2');
}
render() {
return (
<div className="container-fluid">
<div className="row sc-overlay sc-overlay-main">
<div className="col-md-12 col-xl-5 offset-xl-1">
<span className = "sc-overlay-main__left">
<span className = "sc-main-image">
<img src={require('../assets/dialog_logo.png')} />
</span>
<h1 className="mt-4">Welcome to MyDialog</h1>
</span>
</div>
<div className="col-md-12 col-xl-5">
<div className="row sc-overlay-main__right">
<label>Choose your preferred language</label>
<SC_Button label = "සිංහල" btnStyle = "sc-btn-default--sinhala mb-2" onClick={this.handleClick}/>
<SC_Button label = "தமிழ்" btnStyle = "sc-btn-default--tamil mb-2" />
<SC_Button label = "English" btnStyle = "sc-btn-default--english mb-2" />
</div>
</div>
</div>
</div>
);
}
}
export default withRouter(Home);
SC_Button Component
import React from 'react';
import { withRouter } from "react-router-dom";
class SC_Button extends React.Component{
constructor(props) {
super(props);
}
render() {
return (
<button type="button" className={`sc-btn-default ${ this.props.btnStyle }`}>{this.props.label}</button>
);
}
}
export default withRouter(SC_Button);
Your <SC_Button /> component, or any custom component you make, doesn't automatically implement an event handler. You're essentially just giving it yet another prop, called onClick, that it just throws away. You have to use the callback you're passing it in the DOM elements it returns:
SC_Button.js
import React from 'react';
import { withRouter } from "react-router-dom";
class SC_Button extends React.Component{
constructor(props) {
super(props);
}
render() {
return (
<button
type="button"
className={`sc-btn-default ${ this.props.btnStyle }`}
onClick={this.props.handleClick}
>
{this.props.label}
</button>
);
}
}
export default withRouter(SC_Button);
There is no need to define a handleClick function in the component, since you will be passing it as a prop every time you instantiate one. This allows different instances to have different behaviors.
I am very new to React and the ES6 syntax I have a chat widget that i want to toggle to show and hide as i click the header,and I have already implemented an onClick handler, but in terms of the logic I am having trouble finding a similar implementation online. this is my code:
import React, {Component} from 'react';
import chat from './styles.css';
import {connect} from 'react-redux';
class ChatWidget extends Component {
handleClick(event) {
console.log("Hide or unhide chat body")
}
render() {
return (
<div className={chat.container}>
<div onClick={this.handleClick.bind(this)}
className={chat.header}>
<span className={chat.name}>Idol</span>
</div>
<div className={chat.body}>
This is the Body of the chat
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
user: state.activeUser
};
}
export default connect(mapStateToProps)(ChatWidget);
It could look like this:
class ChatWidget extends Component {
constructor(props) {
super(props);
this.state = {
showChat: true
};
}
handleClick(event) {
this.setState({
showChat: !this.state.showChat
});
}
render() {
return (
<div className={chat.container}>
<div onClick={this.handleClick.bind(this)}
className={chat.header}>
<span className={chat.name}>Idol</span>
</div>
{this.state.showChat &&
(<div className={chat.body}>
This is the Body of the chat
</div>)
}
</div>
);
}
}
function mapStateToProps(state) {
return {
user: state.activeUser
};
}
export default connect(mapStateToProps)(ChatWidget);
But there are different approaches for conditional rendering.
See documentation here: https://facebook.github.io/react/docs/conditional-rendering.html
Use a state variable that will store the current state, whether to show the body or not. On the basis of that variable render the body of the Chart, that is called conditional rendering.
Check this:
class ChatWidget extends Component {
constructor(){
super();
this.state={isShowBody: true}
}
handleClick(event) {
this.setState({isShowBody: !this.state.isShowBody})
}
render() {
return (
<div className={chat.container}>
<div onClick={this.handleClick.bind(this)} className={chat.header}>
<span className={chat.name}>Idol</span>
</div>
{this.state.isShowBody ?
<div className={chat.body}>
This is the Body of the chat
</div>
: null}
</div>
);
}
}
What you can do is create a css class
.hidden {
display: none;
}
And dynamically toggle that class on your <div class={chat.body}.
So add an id to the div so it's easier to grab.
<div class={chat.body} id='chat-body'>
...
</div>
And inside your handleClick method
handleClick(event) {
let chat = document.getElementById("chat-body");
chat-body.classList.toggle('hidden')
}
Here you have an example, it has comments on the lines so you know what each line is doing.
Consider that adding a hidden class to the component will not unmount it, it will just hide it from the DOM
import React, {Component} from 'react';
import chat from './styles.css';
import {connect} from 'react-redux';
class ChatWidget extends Component {
// You need state to update the view
state = {
show: false
}
toggleBody() {
// You can pass true or false to this function to show or hide the body
this.setState({show: !this.state.show})
}
render() {
{/* Take the show property from the state */}
const {show} = this.state
return (
<div className={chat.container}>
<div onClick={this.toggleBody.bind(this)} className={chat.header}>
<span className={chat.name}>Idol</span>
</div>
{/* Add the class hidden to the body if show is false (you should create the hidden class in CSS) */}
<div className={`${chat.body} ${show ? '' : 'hidden'}`}>
This is the Body of the chat
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
user: state.activeUser
};
}
export default connect(mapStateToProps)(ChatWidget);
I have a TopNab bar (component), which contains a SearchBar component. My Main component is rendering out TopNav, the main container component (Profile), and the Footer component. I want my SearchBar component to pass its state to the main container component so that the Profile component can use it.
What I am trying to build:
A user types a name into search bar and submits.
The profile matching the user name is displayed in the main container component.
Right now I have a component that can render out user profiles. I also have a component thats state updates to the user submitted value. What I need to do is pass this user submitted value to my profile component in order to render out the correct profile.
Is this possible or do I need to rebuild my components so the search is included in the Profile component?
SearchBar
import React, { Component, PropTypes } from 'react';
import Profile from './Profile';
class SearchBar extends Component {
constructor(props){
super(props)
this.state = {
name: ''
}
}
handleChange(e) {
this.setState({
name: e.target.value
});
}
handleSubmit(e) {
e.preventDefault();
console.log("searching for NAME " + this.state.name);
let profileName = this.state.name;
//PASS STATE TO PROFILE COMPONENT
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit.bind(this)}>
ARMORY BETA
<input type="text" placeholder="Enter Name"
name="name"
value={this.state.name}
onChange={this.handleChange.bind(this)} />
<button className="btn btn-success" type="submit">Search</button>
</form>
</div>
)
}
}
export default SearchBar;
Profile
import React, { Component, PropTypes } from 'react';
import SearchBar from './SearchBar';
import ProfileContainer from '../containers/ProfileContainer';
class Profile extends Component {
render() {
return (
<div className="cols2">
<div>[IMG]</div>
<div>
<ProfileContainer name={this.props.name}/>
</div>
</div>
)
}
}
Profile.PropTypes = {
name: PropTypes.string
}
Profile.defaultProps = {
name: ''
}
export default Profile;
Main
import React, { Component } from 'react';
import TopNav from './TopNav';
import Footer from './Footer';
import Profile from './Profile';
class Main extends Component {
render() {
return (
<div className="container-fluid">
<div className="row">
//TopNav calls SearchBar
<TopNav />
</div>
<div className="row">
<Profile />
</div>
<div className="row">
<Footer />
</div>
</div>
)
}
}
export default Main;
In Main, you should add a prop to <TopNav /> that points to a handler method that will propagate the profileName state change back to Main. This, in turn, will cause Profile to be re-rendered. The handler method takes one argument profileName and is called from the handleSubmit method in TopNav. Here's the code:
SearchBar
class SearchBar extends Component {
. . .
handleSubmit(e) {
e.preventDefault();
console.log("searching for NAME " + this.state.name);
let profileName = this.state.name;
this.props.handleProfileChange(profileName);
}
. . .
}
SearchBar.propTypes = {
handleProfileChange: React.PropTypes.func.isRequired,
}
Main
class Main extends Component {
constructor(props) {
super(props);
this.state = { profileName: '' }
handleProfileChange = this.handleProfileChange.bind(this);
}
handleProfileChange(profileName) {
// This state change will force Profile component to be re-rendered
this.setState( { profileName });
}
render() {
return (
<div className="container-fluid">
<div className="row">
//TopNav calls SearchBar
<TopNav handleProfileChange={this.handleProfileChange} />
</div>
<div className="row">
<Profile profileName={this.state.profileName} />
</div>
<div className="row">
<Footer />
</div>
</div>
)
}
You'll need to expose a property on SearchBar that accepts a callback that will be called to indicate to its parent that the form was submitted (e.g. onSubmit)...
handleSubmit(e) {
e.preventDefault();
console.log("searching for NAME " + this.state.name);
let profileName = this.state.name;
//PASS STATE TO PROFILE COMPONENT
this.props.onSubmit(yourFormData);
}
...TopNav won't handle onSubmit itself, but just pass it on up to its own parent (perhaps renaming to "onSearchBarSubmit" along the way to make the name clearer from the perspective of TopNav's parent):
class TopNav extends Component {
render() {
return (
<div className="container-fluid">
<SearchBar onSubmit={this.props.onSearchBarSubmit}
</div>
);
}
}
class Main extends Component {
render() {
return (
<div className="container-fluid">
<div className="row">
<TopNav onSearchBarSubmit={ (criteria) => this.searchForStuff(criteria) } />
</div>
<div className="row">
<Profile data={this.state.stuffYouGotBackFromSearch} />
</div>
<div className="row">
<Footer />
</div>
</div>
)
}
}
...OR, in some cases, it can be desirable to un-nest the components, allowing SearchBar as one of TopNav's props.children. This allows you to handle onSubmit directly within Main, and pass anything it receives onto Profile:
class Main extends Component {
render() {
return (
<div className="container-fluid">
<div className="row">
//TopNav calls SearchBar
<TopNav>
<SearchBar onSubmit={ (criteria) => this.searchForStuff(criteria) } />
</TopNav>
</div>
<div className="row">
<Profile data={this.state.stuffYouGotBackFromSearch} />
</div>
<div className="row">
<Footer />
</div>
</div>
)
}
}
...a side-benefit of un-nesting is that it would allow you to use TopNav and Searchbar independently.
I stored content component of 4 pages in an array. And I want after clicking next button(in footer component), page layout will render the next content component in the array, does anyone have an idea for that?
Many thanks! Here are my files
**** Content.jsx ****
import React from 'react';
import Start from './Start.jsx';
import Time from './Time.jsx';
import Information from './Information.jsx';
import End from './End.jsx';
export default class Content extends React.Component {
render(){
const Contents = [ <Start />, <Time />, <Information />, <End /> ];
return (
<div className="container">
<div className="row">
{Contents[0]}
</div>
</div>
);
}
};
**** Footer.jsx ****
import React from 'react';
import Content from './Content.jsx';
export default class Footer extends React.Component {
render(){
const button = {
margin: "10em 1em 0 1em"
};
return (
<div className="row">
<div className="col-sm-offset-5 col-sm-2 text-center">
<button className="btn btn-secondary" style={button}>Back</button>
<button className="btn btn-success" style={button}>Next</button>
</div>
</div>
)
}
}
you want to have an array position to know what to transition to.
so in your footer you want to know what the index is and you want to pass the click event through to a parent component to handle the data transfer
export default class Footer extends Component {
viewNext(e){
e.preventDefault();
this.props.setContent(this.props.currentIndex + 1);
}
viewPrevious(e){
e.preventDefault();
this.props.setContent(this.props.currentIndex - 1);
}
render(){
const button = {
margin: "10em 1em 0 1em"
};
return (
<div className="row">
<div className="col-sm-offset-5 col-sm-2 text-center">
<button className="btn btn-secondary" style={button} onClick={this.viewPrevious.bind(this)}>Back</button>
<button className="btn btn-success" style={button} onClick={this.viewNext.bind(this)}>Next</button>
</div>
</div>
)
}
}
now, when you are rendering the footer you want to pass through the relative info
class App extends Component {
constructor(props){
super(props);
this.state = {currentIndex: 0};
this.setContent = this.setContent.bind(this);
}
setContent(index){
this.setState({currentIndex: index});
}
render(){
let {currentIndex} = this.state;
return (
<div>
<Content currentIndex={currentIndex}/>
<Footer setContent={this.setContent} currentIndex={currentIndex}/>
</div>
)
}
}
finally in the content component you need to accept this index and use it as well
export default class Content extends Component {
constructor(props){
super(props);
this.componentsArr = [ <Start />, <Time />, <Information />, <End /> ]; // I moved this array to the constructor so this way you aren't creating it every single render. just need to make it once.
}
render(){
const ViewComponent = this.componentsArr[this.props.currentIndex];
return (
<div className="container">
<div className="row">
{ViewComponent}
</div>
</div>
);
}
}
So here is some homework for you. Add validation on viewing the next component or the previous. meaning what happens when you are viewing the last component (<End />) and you click the next button? Hint: You need to move the componentsArr logic to App as well and pass its length to the footer for index position purposes.
In parent component of both Content and Footer add a method setActiveContent.
class Parent extends React.component {
constructor(props) {
super(props);
this.state = {
activeContent: ''
}
}
setActiveContent(contentId) {
this.setState({
activeContent: this.state.activeContent + contentId
});
}
render(){
<div>
<Content activeContent={this.state.activeContent}/>
<Footer setActiveContent={this.setActiveContent}/>
}
}
export default class Footer extends React.Component {
handleClick() {
this.props.setActiveContent(1);
}
render(){
const button = {
margin: "10em 1em 0 1em"
};
return (
<div className="row">
<div className="col-sm-offset-5 col-sm-2 text-center">
<button className="btn btn-secondary" style={button}>Back</button>
<button onClick={this.handleClick} className="btn btn-success" style={button}>Next</button>
</div>
</div>
)
}
}
export default class Content extends React.Component {
render(){
const Contents = [ <Start />, <Time />, <Information />, <End /> ];
return (
<div className="container">
<div className="row">
{Contents[this.props.activeContent]}
</div>
</div>
);
}
};