How to load new component on button click - reactjs

I have a form, when user taps on Login button, i want to load a new component, and disable the current component. I read about react-router,but it looks like i can use them for navigation.
This is my code.
class App extends Component {
render() {
return (
<div className="App">
<HeaderComponent />
<header className="App-header">
<LoginComponent/>
</header>
</div>
);
}
when user taps on Login button, this method in LoginComponent.js gets called.
handleSubmit = event => {
event.preventDefault();
console.log("Login");
}
Now i can successfully prints the Log message, but i am unable to understand how to load a different component at this time.

If you don't want it to be on a separate page you can use a conditional in your render, and change the state on the submit.
Example:
class App extends Component {
state = {
componentShown: false
}
render() {
return (
<div className="App">
<HeaderComponent />
<header className="App-header">
{this.state.componentShown ? <ComponentToRender /> :
<LoginComponent/> }
</header>
</div>
);
}
And on your submit just change the state.
handleSubmit = event => {
event.preventDefault();
this.setState({ componentShown: true })
}

Related

how to disable a button in React imported into another component?

it's my first application in react and I'm not sure how to disable an imported button.
I have a component button that I import into a parent component
import React, { Component } from "react";
import "../../index.scss";
class Submit extends Component {
render() {
return (
<button className="button"
onClick={() => this.props.onClick()}>
SUBMIT
</button>
);
}
}
export default Submit;
in the component that rendered it is as follows
renderSubmit() {
return (
<Submit
onClick={() => this.submitForm()}
/>
);
}
render() {
return (
<div className="table">
<div className="table-actions">
{this.renderRefresh()}
{this.renderSubmit()}
</div>
</div>
);
}
}
I have tried to set the class to disabled from the original component but it depends on a state property and does not recognize it.
import React, { Component } from "react";
import "../../index.scss";
class Submit extends Component {
render() {
return (
<button className="button"
disabled={this.state.start}
onClick={() => this.props.onClick()}>
SUBMIT
</button>
);
}
}
export default Submit;
How can I condition the disabled state to a state property?
Your Submit button doesn't allow for setting any other props on the underlying button component. It should proxy though any props you want to be externally configured by what is rendering the Submit button. I also suggest explicitly declaring the button type to be "submit", or also exposing that prop out in the component API.
Your proxying of the onClick handler also drops the click event, that should be passed through in case any consuming component care about it.
class Submit extends Component {
render() {
const { disabled, onClick, type = "submit" } = this.props;
return (
<button
className="button"
disabled={disabled}
onClick={onClick}
type={type}
>
SUBMIT
</button>
);
}
}
For such a simple component with no internal logic IMO a functional component is a better option, and I would name it more clearly.
const SubmitButton = ({ disabled, onClick, type = "submit" }) => (
<button
className="button"
disabled={disabled}
onClick={onClick}
type={type}
>
SUBMIT
</button>
);
Now when you are using the submit button from a parent component you can pass in a disabled prop based on any condition you need/require.
render() {
const { submitDisabled } = this.state;
return (
<div className="table">
<div className="table-actions">
{this.renderRefresh()}
<SubmitButton
disabled={submitDisabled} // <-- pass disabled value
onClick={this.submitForm} // <-- attach click handler
type="button" // <-- so we don't accidentally take default form action
/>
</div>
</div>
);
}
}
How you compute/set this.state.submitDisabled is up to you. Maybe it is disabled when the form is being submitted, for example.
submitForm = () => {
this.setState({ submitDisabled: true });
...
};

HandleClick inside and outside of DOM Element

I want to close the menu when there's a click on <Burger /> and when there's a click outside <Burger />. With my current code only the click outside <Burger /> will be handled, but the click on <Burger /> will not close the menu.
When I reverse domNode to !domNode in the handleClickOutside function, clicking inside <Burger /> works, but clicking outside <Burger /> will no longer close the menu. Also I tried adding other conditions to the handleClickOutside function and that did not work also.
What am I missing?
Thanks!
export class NavBar extends React.Component {
constructor(props) {
super(props);
this.state = {
change: false
};
this.handleClick = this.handleClick.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
}
componentDidMount() {
document.addEventListener('click', this.handleClickOutside, true);
}
componentWillUnmount(){
document.removeEventListener('click', this.handleClickOutside, true)
}
handleClick() {
this.setState(state => ({
change: !state.change
}));
}
handleClickOutside(event) {
const domNode = ReactDOM.findDOMNode(this);
if(domNode || !domNode.contains(event.target)){
this.setState(state=> ({
change: false
}))
}
}
render() {
return (
<div>
<div className="mobile">
<Menu change={this.state.change} onClickOutside={this.handleClickOutside} />
<nav>
<Burger change={this.state.change} onClick={this.handleClick} />
</nav>
</div>
</div>
)
}
}
<Burger />
export class Burger extends React.Component {
render(){
return(
<a className={classnames('burger', {'change': this.props.change})} onClick={this.props.onClick}>
<div/>
<div/>
<div/>
</a>
)
}
}
<Menu />
export class Menu extends React.Component {
render(){
return(
<div className={classnames({change: !this.props.change})}>
<div className="menu" onClick={this.props.onClickOutside}>
<Link>
</Link>
<Link>
</Link>
</div>
</div>
)
}
}
I can't be sure since i haven't all your code, but I have a guess.
You added an event listener in componentDidMount:
componentDidMount() {
document.addEventListener('click', this.handleClickOutside, true);
}
With this code, depending on how Burger handle its onClick props, this may happen:
You click inside the Burger; its onClick is triggered which calls the props.onClick and thus you call the handleClick of NavBar;
Then, since the event propagates, it reaches the handleClickOutside, and that's because the event has reached the document. So, since you call handleClickOutside, inside of it you switch again the state.
Again, this is all a guess I'm having. If you wanna test this you can use the debug and notice if, when clicking inside the Burger, you reach both onClick and handleClickOutside.
Anyways, it would be appreciated the code of both Burger and Menu because we cannot know what is happening in there.

reactjs conditional render works after second click

I have three components: MyDashboard, MyContent, MyForm.
The MyDashboard component starts off by rendering the MyContent component as a child. I have a button within the MyContent component which when clicked should swap out the component with the MyForm component.
Clicking the button flashes the MyForm component on the first click, then goes straight back to the MyContent component. After the second click however the MyForm component is rendered and stays rendered.
MyDashboard:
function MyDashboard(props) {
const [viewForm, setViewForm] = useState(false); // Don't show the form initially
const handleClick = () => {
setViewForm(!viewForm);
};
return (
<div>
{(() => {
if (viewForm) { // conditionally render the form or the content
return <MyForm {...props} />;
} else {
return (
<MyContent
{...props}
setViewForm={handleClick}
viewForm={viewForm}
/>
);
}
})()}
</div>
);
}
MyContent:
function MyContent(props) {
return (
<p>
<a href="#" onClick={props.setViewForm}> // need to click this twice to render the form.
Show the form
</a>
</p>
);
}
export default MyContent;
MyForm:
function MyForm({ props, location }) {
// form setup
return (
<div>
<Row>
<Form>
//... the form
</form>
</Row>
</div>
);
}
export default MyForm;

Reuse a react component with different method calls

I want to create a component with a simple form, lets say with 4 input fields and a button to submit. When the user clicks submit, a POST request will be made.
But then I want to reuse the same component to make a PUT request. How can I achieve that?
Pass a function from a parent component to you child component (the form):
https://codesandbox.io/s/4x3zjwxwkw
const App = () => (
<div>
<CreateRecipe />
<UpdateRecipe />
</div>
);
class CreateRecipe extends React.Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(e) {
e.preventDefault();
console.log("Do POST recipe");
}
render() {
return (
<div>
<MyForm title="POST Recipe" onSubmit={this.onSubmit} />
</div>
);
}
}
class UpdateRecipe extends React.Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(e) {
e.preventDefault();
console.log("Do GET recipe");
}
render() {
return (
<div>
<MyForm title="GET Recipe" onSubmit={this.onSubmit} />
</div>
);
}
}
const MyForm = ({ title, onSubmit }) => {
return (
<div>
<h2>{title}</h2>
<form onSubmit={onSubmit}>
<input type="text" />
<button type="submit">Submit</button>
</form>
</div>
);
};
EDIT: Per the comment, you can absolutely separate the POST and GET submit functions, but this duplicates logic a bit. What could be better is to have the <App /> component own the two submit functions, and pass them respectively to <UpdateRecipe /> and <PostRecipe />, maybe even the titles too! I'll let you compose this however you'd like, but hopefully this shows you the flexibility of React.
You can pass in a method as a prop to the component - that way you can define the functionality outside of the component and let it execute it within the components scope.
An example of this would be passing in a onPress event to a Button, or an onClick event to any other component.
You can pass the method into the component as a prop and then reference props in your submit function like this:
submit = () => {
var data = {
method: this.props.method,
...
};
fetch('/url/', data);
}

Passing state in React from two different components

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.

Resources