React slide in navigation on button click - javascript

I am starting to work with React and trying to get my head around it. I am currently trying to build a navigation component that slides in when a button is clicked (the button lives in another component).
Here is my code so far,
class Application extends React.Component {
constructor() {
super();
this.state = {
sidebarOpen:false
}
}
handleViewSidebar() {
this.state.sidebarOpen = !this.state.sidebarOpen;
}
render() {
return(
<div>
<Navigation isOpen={this.state.sidebarOpen} toggleSidebar={this.handViewSidebar}/>
<Header isOpen={this.state.sidebarOpen} />
</div>
);
}
}
class Header extends React.Component {
constructor(props) {
super(props);
this.slideMenu = this.slideMenu.bind(this);
}
render() {
return(
<header>
<div className="container">
<h1><a>AppName</a></h1>
<div className="user__actions">
<a>Notifications</a>
<a onClick={this.slideMenu}>Menu</a>
</div>
</div>
</header>
);
}
slideMenu() {
this.setState({sidebarOpen:true});
console.log(this.state);
}
}
class Navigation extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<nav className={(this.props.sidebarOpen ? "site__navigation visible" : "site__navigation")}>
<a>Friends</a>
<a>Matches</a>
<a>Messages</a>
<a>Profile</a>
<a>Search</a>
</nav>
)
}
}
/*
* Render the above component into the div#app
*/
React.render(<Application />, document.getElementById('app'));
What I am finding is that one state is passed through all my components? In my slideMenu function i console log this.state but it is null. I cannot work out how to add a class to Navigation componenet on click of button to make the nav bar visible?

class Application extends React.Component {
constructor() {
super();
this.state = {
sidebarOpen:false
}
}
handleViewSidebar() {
this.setState({sidebarOpen:!this.state.sidebarOpen});
}
render() {
return(
<div>
<Navigation isOpen={this.state.sidebarOpen} toggleSidebar={this.handleViewSidebar.bind(this)}/>
<Header isOpen={this.state.sidebarOpen} toggleSidebar={this.handleViewSidebar.bind(this)}/>
</div>
);
}
}
class Header extends React.Component {
render() {
return(
<header>
<div className="container">
<h1><a>AppName</a></h1>
<div className="user__actions">
<a>Notifications</a>
<a onClick={this.props.toggleSidebar}>Menu</a>
</div>
</div>
</header>
);
}
}
class Navigation extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<nav className={(this.props.isOpen === true ? "site__navigation visible" : "site__navigation")}>
<a>Friends</a>
<a>Matches</a>
<a>Messages</a>
<a>Profile</a>
<a>Search</a>
</nav>
)
}
}
This will work for you, there were little mistakes which i corrected for you

Your example code is not working mostly because of small mistakes such as :
Directly assigning state (which will not call render to update your application). You need to update your app through setState() calls.
handleViewSidebar() {
this.state.sidebarOpen = !this.state.sidebarOpen;
}
Should be
handleViewSidebar() {
this.setState({sidebarOpen: !this.state.sidebarOpen});
}
and passing props with different names but using them with the initial name. Example: sidebarOpen vs isOpen
You also don't need "slideMenu" as you can pass handleViewSidebar as a prop and call it directly from the Header component.

Related

How to create a List/Detail View in React

I need to implement a kind of Master/Detail View for a Web Application in React. Since the app should be integrated into a CakePHP app I can't use React Router for handling the routes (since CakePHP would process them).
I have a List of Items and want to navigate through them, showing a Detail View. Items are nested, so there're SubItems to navigate to.
For now I got a ItemList Component, showing a list of Cards with a clickhandler. How can I change the View without changing the url?
ItemList Component looks like:
class ItemList extends React.Component {
constructor(props) {
super(props);
this.state = {
itemList: []
}
}
componentDidMount() {
fetchItems(...)
}
render() {
return(
<div>
{this.state.itemList.map(item => (
<Item key={item.id} item={item} />
))}
</div>
);
}
}
Item Component looks like:
class Item extends React.Component {
constructor(props) {
super(props);
this.state = {
item: props.item,
}
}
handleClick = () => {
// How to navigate to another Component?
}
render() {
return(
<div>
<div className="card my-2" onClick={this.handleClick}>
<div className="card-body">
<h5 className="card-title">{this.state.item.title}</h5>
<p className="card-text">{this.state.item.description}</p>
</div>
</div>
</div>
);
}
}
Thanks in advance!
You should have a parent component (let's say MainView) that has a state (let's say selectedItemId).
class MainView extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedItemId: [null]
}
}
componentDidMount() {
}
render() {
return(
{!selectedItemId && (<ItemList />)}
{selectedItemId && (
<ItemDetail id={selectedItemId} />
)}
);
}
}
As you can see, it renders different components based on the selectedItemId state value.
Inside the ItemList handleClick you call the setState of the parent MainView to set the selected item ID.
So using conditional rendering inside the render() function of MainView you can render the ItemList when no item is selected and ItemDetail when you have selected one.
I'm not really used to ES6 syntax components so my code can be wrong somewhere, but you can get the message ;)

React Loading New Page

I am trying to load a different React component using a button. It worked when doing it for authentication with GitHub using Firebase, but won't work for this page.
import React from 'react';
import './index.css';
import GamePage from '../Game';
class Home extends React.Component {
constructor(props){
super(props);
this.LoadGamePage = this.LoadGamePage.bind(this);
}
LoadGamePage() {
return(
<div>
<GamePage />
</div>
)
}
render(){
return(
<div className="home">
<h1>Home Page</h1>
<button onClick={this.LoadGamePage}>Play PIT</button>
</div>
)
}
}
export default Home;
Is there something wrong with my LoadGamePage function?
How it is supposed to work? You have an onclick handler, which calls a class method. That class method, called LoadGamePage, returns JSX. Okey, but what now? It is returned, but... not rendered. It won't display anywhere. What would I suggest you? Instead of returning the JSX inside that handler, I would set state and depending on state I would render the Game Page or not.
class Home extends React.Component {
constructor(props){
super(props);
this.state = {
gameVisible: false,
}
this.LoadGamePage = this.LoadGamePage.bind(this);
}
LoadGamePage() {
this.setState({ gameVisible: true });
}
render() {
if (this.state.gameVisible) {
return <GamePage />
}
return (
<div className="home">
<h1>Home Page</h1>
<button onClick={this.LoadGamePage}>Play PIT</button>
</div>
)
}
}

React.js render subcomponents from component

I want to create a reusable component where the DOM structure can be different each time the component is rendered. Let's say I have this
class Comp extends React.Component {
constructor() {
super()
this.state = {
click: null,
}
}
render() {
return(
<div>
{this.props.chidren}
</div>
)
}
handleButton1() {
this.setState({click: 'button1'});
}
handleButton2() {
this.setState({click: 'button2'});
}
}
class SubComp1 extends React.Component {
render() {
return(
<button onClick={() => this.props.handleButton1()}>Button 1</button>
)
}
}
class SubComp2 extends React.Component {
render() {
return (
<button onClick={() => this.props.handleButton2()}>Button 2</button>
)
}
}
ReactDOM.render((
<Comp>
<div id="somediv">
<div id="andanother">
<SubComp1 />
</div>
</div>
<div id="andanotherother">
<SubComp2 />
</div>
</Comp>), document.getElementById('app'))
Currently, the two subcomponents do not have access to their respective handler functions. What's the best way of passing the functions handleButton1 and handleButton2 to the subcomponents assuming that their position in the DOM is dynamic and might change depending on the layout of the page.
I have thought of 2 solutions so far:
Iterating inside the props.children until I find the element of interest then clone it with the property
Using ref and somehow render the subcomponents after the main component has been rendered through the componentDidMount callback.
What are your thoughts on this?
This is a place where using React's Context would be the most straightforward solution.
Another solution would be to use Redux actions, but that would make your component less reusable and more tightly coupled with your application, which you may or may not care about.
Why not do something like this:
class Comp extends React.Component {
constructor() {
super()
this.state = {
click: null,
}
}
render() {
return(
<div>
{this.props.chidren}
</div>
)
}
handleButton(button) {
this.setState({click: button});
}
}
Then in the subcomponents you can do something like
class SubComp1 extends React.Component {
render() {
return(
<button onClick={() => this.props.handleButton('button1')}>Button 1</button>
)
}
}
class SubComp2 extends React.Component {
render() {
return (
<button onClick={() => this.props.handleButton('button2')}>Button 2</button>
)
}
}
One Alternative option which might fit your needs is to build a higher order component, which decorates another component with some additional functionality, below is a quick example of how this may work for you,
The higher order component:
const Comp = ComposedComponent =>
class Comp extends React.Component {
constructor(props) {
super(props)
this.handleButton = this.handleButton.bind(this);
this.state = {
click: null,
}
}
handleButton(button) {
this.setState({click: button});
}
render() {
return(
<ComposedComponent
onClick={this.handleButton}
/>
)
}
}
export default Comp;
The child component:
class SubComp1 extends React.Component {
render() {
return(
<button onClick={() => this.props.onClick('button1')}>Button 1</button>
)
}
}
How to use it:
const ExtendedComp = Comp(SubComp1);
<ExtendedComp />
would this be suitable for your task?

Show sibling components reactJs

I'm new on react world, I would show components from sibling components.
I have parent component:
import Toast from './components/Toast/Toast'
class App extends Component {
constructor(){
super();
this.state = {
showToast:false
};
}
render() {
return (
<div id="cont">
<Toast showToast={this.state.showToast}/>
<Header />
</div>
);
}
}
In my Toast component:
class Toast extends Component {
constructor(props) {
super(props);
}
render() {
const showToast = this.props.showToast;
let toast = null;
if (showToast) {
toast = <div className="visible">Toast Ok</div>;
}else{
toast = null;
}
return (
<div>
{toast}
</div>
);
}
}
export default Toast;
And in my Header component I have:
class Header extends Component {
render() {
return (
<button> // With click, show toastComponents so setState parent </button>
)
}
So if I click on button I would set state key showToast for show my components.
You can pass a function down to your <Header> component, then call it when the button is clicked.
let showToast = () => this.setState({ showToast: true });
// ...
<Toast showToast={this.state.showToast}/>
<Header onClick={showToast}>
Then all you need to do is pass this prop through to the click handler inside <Header>.
<button onClick={this.props.onClick}>

How to call a component method from another component?

I have a header component that contain a button and I want this button to display another component(modal page) when it's clicked.
Can I do something like this:
Here's my header component:
import ComponentToDisplay from './components/ComponentToDisplay/index'
class Header extends React.Component {
constructor(props) {
super(props)
}
props : {
user: User
}
_handleInvitePlayerClick = () => {
this.refs.simpleDialog.show();
}
render(){
return(
<Button onClick={this._handleInvitePlayerClick} ><myButton/></Button>
<ComponentToDisplay />
)
}
}
Here is my component for the modal page that should be displayed when the button on the other component gets clicked:
class ComponentToDisplay extends React.Component {
componentDidMount() {
}
render() {
return (
<div>
<SkyLight
ref="simpleDialog"
title={"Title for the modal"}>
{"Text inside the modal."}
<Button onClick={() => this.refs.simpleDialog.hide()}>{"Close modal"}</Button>
</SkyLight>
</div>
)
}
}
Library being used for the modal : https://github.com/marcio/react-skylight
More like this:
class Header extends React.Component {
constructor(props) {
super(props)
}
props: {
user: User
}
render() {
return (
<Button onClick={() => this.refs.componentToDisplay.showMe()}><myButton /></Button>
<ComponentToDisplay ref="componentToDisplay" />
)
}
}
Being sure to expose a showMe() method on your child component:
class ComponentToDisplay extends React.Component {
showMe() {
this.refs.simpleDialog.show();
}
render() {
return (
<div>
<SkyLight
ref="simpleDialog"
title={"Title for the modal"}>
{"Text inside the modal."}
<Button onClick={() => this.refs.simpleDialog.hide()}>{"Close modal"}</Button>
</SkyLight>
</div>
)
}
}
Basically, what's going on here is you wrap the SkyLight's show() method in your child component's own method (in this case, showMe()). Then, in your parent component you add a ref to your included child component so you can reference it and call that method.

Categories