ReactJs component structure - Form inside modal - javascript

I am using the react-bootstrap Modal, Form and Button.
Desiring the functionality of clicking the button should open the modal with a form inside it. After filling out the form, one clicks a button (on the modal) and it validates the form data and posts it through a REST API.
I got far enough to figure out that my component split should be as follows:
A button component, a modal component and a form component.
What would be the correct way to structure these components in terms of props/state and placing the functions for validating the data? I am having trouble in understanding the child/parent relationship and when it's applicable

Components:
App Component: This is going to be the top level component
Button Component (If its just a button can also be
just a button):
If this is just a button you can keep this has a just a button in App component, if you are willing to reuse this with some custom element place it in a component.
Modal component: This is going to hold your modal like header,body,footer
Form component: This is a component which will hold the form and its validations.
Component Tree:
App Component will contain a state like showModal, we need to have a handler to set this value and this handler gets triggered when the button is clicked.
import FormModal from './FormModal';
class App extends React.Component {
state = {
showModal : false
}
showModalHandler = (event) =>{
this.setState({showModal:true});
}
hideModalHandler = (event) =>{
this.setState({showModal:false});
}
render() {
return (
<div className="shopping-list">
<button type="button" onClick={this.showModalHandler}>Click Me!
</button>
</div>
<FormModal showModal={this.sate.showModal} hideModalHandler={this.hideModalHandler}></FormModal>
);
}
}
Form Modal:
import FormContent from './FormContent';
class FormModal extends React.Component {
render() {
const formContent = <FormContent></FormContent>;
const modal = this.props.showModal ? <div>{formContent}</div> : null;
return (
<div>
{modal}
</div>
);
}
}
export default FormModal;
Hope that helped!

For basic pseudo code
Main Component:
import Modal from './Modal'
class Super extends React.Component {
constructor(){
this.state={
modalShowToggle: false
}
}
ModalPopUpHandler=()=>{
this.setState({
modalShowToggle: !modalShowToggle
})
}
render(){
return(){
<div>
<Button title='ModalOpen' onClick='this.ModalPopUpHandler'> </Button>
<ModalComponent show={this.state.modalShowToggle}>
</div>
}
}
}
ModalPopUp component:
import FormComponent from 'FormComponent'
class ModalComponent extends React.Component {
constructor(props){
super(props)
this.state={
modalToggle: props.show
}
}
render(){
if(this.state.modalToggle){
return(
<div>
<div className='ModalContainer'>
<FormComponent />
</div>
</div>
)
} else {
</div>
}
}
}
Form Component:
import Button from './Button'
class FormComponent extends React.Component {
constructor(){
this.state={
submitButtonToggle: true,
username: ''
}
}
inputHandler=(e)=>{
if(e){
this.setState({
username: e.target.value
})
}
}
render(){
return(
<div>
<input type='text' value='this.state.username' id='username' onChange='inputHandler' />
<Button title='Submit' disabled={this.state.username.length > 0}> </Button>
</div>
)
}
}
Above are the basic superComponent which we have rendered in app/main entry file.
And form || Modal Component. are the child component.
So in modal component I have called the same Form-component.
Here in Form-component input type handler, submit button is disabled from state.. with input string length we are handling its validation.
I hope it works for you.

Related

how to call functional component in class based component using onClick event?

i want to show my functional component in class base component but it is not working. i made simpletable component which is function based and it is showing only table with some values but i want to show it when i clicked on Show user button.
import React ,{Component} from 'react';
import Button from '#material-ui/core/Button';
import SimpleTable from "../userList/result/result";
class ShowUser extends Component{
constructor(props) {
super(props);
this.userList = this.userList.bind(this);
}
userList = () => {
//console.log('You just clicked a recipe name.');
<SimpleTable/>
}
render() {
return (
<div>
<Button variant="contained" color="primary" onClick={this.userList} >
Show User List
</Button>
</div>
);
}
}
export default ShowUser;
Why your code is not working
SimpleTable has to be rendered, so you need to place it inside the render method. Anything that needs to be rendered inside your component has to be placed there
On Click can just contain SimpleTable, it should be used to change the value of the state variable that controls if or not your component will be shown. How do you expect this to work, you are not rendering the table.
Below is how your code should look like to accomplish what you want :
import React ,{Component} from 'react';
import Button from '#material-ui/core/Button';
import SimpleTable from "../userList/result/result";
class ShowUser extends Component{
constructor(props) {
super(props);
this.state = { showUserList : false }
this.userList = this.userList.bind(this);
}
showUserList = () => {
this.setState({ showUserList : true });
}
render() {
return (
<div>
<Button variant="contained" color="primary" onClick={this.showUserList} >
Show User List
</Button>
{this.state.showUserList ? <SimpleTable/> : null}
</div>
);
}
}
export default ShowUser;
You can also add a hideUserList method for some other click.
Or even better a toggleUserList
this.setState({ showUserList : !this.state.showUserList});
If you're referring to the method userList then it appears that you're assuming there is an implicit return value. Because you're using curly braces you need to explicitly return from the function meaning:
const explicitReturn = () => { 134 };
explicitReturn(); <-- returns undefined
const implicitReturn = () => (134);
implicitReturn(); <-- returns 134
The problem lies with how you are trying to display the SimpleTable component. You are using it inside the userList function, but this is incorrect. Only use React elements inside the render method.
What you can do instead is use a state, to toggle the display of the component. Like this:
const SimpleTable = () => (
<p>SimpleTable</p>
);
class ShowUser extends React.Component {
constructor(props) {
super(props);
this.state = {showSimpleTable: false};
this.toggle= this.toggle.bind(this);
}
toggle = () => {
this.setState(prev => ({showSimpleTable: !prev.showSimpleTable}));
}
render() {
return (
<div>
<button variant = "contained" color = "primary" onClick={this.toggle}>
Show User List
</button>
{this.state.showSimpleTable && <SimpleTable />}
</div>
);
}
}
ReactDOM.render(<ShowUser />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
The functionality you are looking for is called Conditional Rendering. The onClick prop function is an event handler and events in react may be used to change the state of a component. That state then may be used to render the components. In normal vanilla javascript or jQuery we call a function and modify the actual DOM to manipulate the UI. But React works with a virtual DOM. You can achieve the functionality you are looking for as follows:
class ShowUser extends Component {
constructor(props) {
super(props)
// This state will control whether the simple table renders or not
this.state = {
showTable: false
}
this.userList.bind(this)
}
// Now when this function is called it will set the state showTable to true
// Setting the state in react re-renders the component (calls the render method again)
userList() {
this.setState({ showTable: true })
}
render() {
const { showTable } = this.state
return (
<div>
<Button variant="contained" color="primary" onClick={this.userList}>
Show User List
</Button>
{/* if showTable is true then <SimpleTable /> is rendered if falls nothing is rendered */}
{showTable && <SimpleTable />}
</div>
)
}
}

Page refreshes when I click submit ReactJS

I have been trying to implement a scenario where the data is passed from the child component to the parent component.
I am able to pass it but the screen doesn't persist due to the use of the form. I want to know how I can implement it while using form.
import React, { Component } from 'react';
import './App.css';
class FormComponent extends React.Component {
render() {
return (
<form>
<input type="text" name="caption" value={this.props.caption} onChange={(event) => this.props.changeCaption(event.target.value)} />
<button onClick={this.props.onClick}>Add</button>
</form>
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
caption: ""
}
}
changeCaption = (caption) => {
this.setState({
caption: caption
})
}
handleClick = () => {
console.log('Send to the list component', this.state.caption);
}
render() {
return (
<div className="App">
<header className="App-header">
<FormComponent
onClick={this.handleClick}
caption={this.state.caption}
changeCaption={this.changeCaption}
/>
</header>
</div>
);
}
}
export default App;
I would really appreciate some help with this. Thank You.
Hard to tell without testing but let me guess that it's link to "normal" behaviour of a form tag when submitted ....
Then you'll have to prevent the event to bubble, try this :
handleClick = (e) => {
e.preventDefault();
console.log('Send to the list component', this.state.caption);
}
if you don't want to submit form on click you can add type="button" attribute to your button:
<button type="button" onClick={this.props.onClick}>Add</button>

React component re-renders endlessly with onClick

I want the onClick event of the button in result.js to render my Spinner component, and have so far (kind of) gotten it to do so. At the moment, Spinner mostly has some console.log() statements, and it keeps logging "Rendered spinner." endlessly after clicking the button, about once every second.
For the record, the returned paragraph isn't being displayed, but I haven't gotten around to debugging that yet. Also, I have excluded some code in Result.js that I think is irrelevant.
For now, I just want Spinner to only render once after pressing the button. Any tips?
result.js:
import React, { Component } from "react";
import { connect } from "react-redux";
import Spinner from "./spinner";
class UnboxResult extends Component {
constructor(props) {
super(props);
this.state = {
showSpinner: false
};
this.handleUnboxClicked = this.handleUnboxClicked.bind(this);
}
handleUnboxClicked(event) {
event.preventDefault();
console.log("Inside handleUnboxClicked");
this.setState({
showSpinner: true
});
}
render() {
return (
<section className="opening">
<div className="container">
<div className="row">
<button onClick={this.handleUnboxClicked}>UNBOX</button>
</div>
<div className="row">
{this.state.showSpinner ?
<Spinner items={this.props.unbox.items}/> :
null}
</div>
</div>
</section>
);
}
}
export default connect(state => ({
unbox: state.unbox
}))(UnboxResult);
spinner.js:
import React, { Component } from 'react';
class Spinner extends Component {
constructor(props) {
console.log("Before super");
super(props);
console.log("Ran constructor.");
}
render(){
console.log("Rendered spinner.");
return(
<p>Spinning..</p>
);
}
}
export default Spinner;
You could add a handler method to update the state from spinner
handleClick(){
this.setState({
showSpinner: true
})
}
and in your render it will need to be passed as prop
<div className="row">
{this.state.showSpinner ?
<Spinner handleClick={this.handleClick}/> :
null}
</div>
In your spinner component return you can trigger this using onclick
<button onClick = {this.props.handleClick} > Click </button>
This will allow you to update the state back in your parent, You might want to figure out how you would display the items one at a time in spinner and only set state to false when there is no items left to display.
Sorry if i misunderstood your comment.

React - How to pass data among parent - child AND among siblings at the same time?

I have a component (LoginScreen). In that component I want to display my Login component as the first thing the user sees. When user clicks on sign up button, in my Loginscreen component, the Signup Component should be rendered instead. From the signup Component the user finds a button 'Back to Login' and when clicked, again the Login Component should be rendered insight the componentt Loginscreen. Im new to React and trying to follow tutorials about how to share data among parent/child and among siblings but am completely confused. Any help would be amazing!
class Loginscreen extends React.Component{
constructor(props) {
super(props)
this.state = {
status:false
}
this.changeStatus = this.changeStatus.bind(this);
}
changeStatus(status) {
this.setState({
status: true
});
}
render() {
return (
<div>
<Login status={this.state.status}/>
<Signup status={this.state.status}/>
<p>No account yet?</p>
<button onClick={this.changeStatus}>Sign up</button>
// if Signup Component is rendered insight Loginscreen, then this button should also not be rendered.
</div>
)
}
}
class Signup extends React.Component {
...
//where can I even call this function in my case?
handleChange() {
const status:true;
this.props.onClick(status);
}
...
<button><Link to='/loginscreen'>Back to Login</Link></button>
...
}
class Login extends React.Component {
...
...
}
Ok, I believe you are looking for routing?
Solution 1 (recommended):
Using React-Router to handle the routing and the React-Router/Link component will handle the switching.
Solution 2:
Using a simple state routing, saving the view name in the parent component and display the view based on it, also passing a function to update this view:
class App extends React.Component{
constructor(props) {
super(props)
this.state = {
view: 'login' // its login because we want it to be the default
}
this.changeView = this.changeView.bind(this);
}
changeView(view) {
this.setState({
view // ES6, if the key & value variable name the same, just type it once.
});
}
render() {
const { view } = this.state; // thats es6 destructuring, use it to make the variables clean instead of repeating the this.state/this.props
return (
<div>
{
view == 'login'
? (<Login changeView={this.changeView}/>)
: (<Signup changeView={this.changeView}/>)
}
</div>
)
}
}
class Signup extends React.Component {
...
render(){
const { changeView } = this.props;
<div className="Signup">
{/* Signup Form Here */}
<p>Already registered?</p>
{/* Wrapping the event in arrow function to avoid auto invoke */}
<button onClick={() => changeView('login')}>Login</button>
</div>
}
...
}
class Login extends React.Component {
...
render(){
const { changeView } = this.props;
<div className="Login">
{/* Login Form Here */}
<p>No account yet?</p>
<button onClick={() => changeView('signup')}>Sign up</button>
</div>
}
...
}
If there are more than 2 views you can wrap the return in a normal If statement, or move it in a separate method.
or you can use a dynamic component rendering, something like this:
render() {
const { view } = this.state;
const ViewComponent = require(`./views/${view}.jsx`);
return (<div><ViewComponent.default changeView={this.changeView} /></div>);
}

Passing data to parent in React

I'm new to React and thus the question.
I have a parent Component - HomePage with a child Component - SideBar.
My child component sidebar needs to pass a data back to the parent on a submit button click which the parent needs to post on an api.
This my parent component,
class HomePage extends React.Component{
constructor(props) {
.......
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(){
//Logic to get the data from the child and post to localhost:8080/
}
render(){
return(
<div className="col-md-2 left-pane">
<Sidebar handleSubmitButton={this.state.handleSubmit}/>
</div>
);
}
}
This is my child component,
class Sidebar extends React.Component {
handleSubmitButton(event) {
event.preventDefault();
}
render() {
return (
<div>
<input type="text"/>
<button type="button" className="btn btn-info btn-icons" onClick={this.props.handleSubmitButton} >
<span className="glyphicon glyphicon-send" aria-hidden="true"/>
</button>
</div>
);
}
}
Sidebar.propTypes = {
handleSubmitButton: React.PropTypes.func
};
My question is how do I grab the input text with the onclick method on the sidebar button click and pass it up to the parent to post it on an api. Any help appreciated.
Your parent Component should not access the input directly, but rather rely on the child component to pass any values to the parent when required.
class HomePage extends React.Component {
constructor(props) {
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(textInputValue){
// The callback passed to the child component will
// submit the data back to it's parent.
// Logic to post to localhost:8080/
}
render(){
return(
<div className="col-md-2 left-pane">
<Sidebar handleSubmitButton={ this.handleSubmit }/>
</div>
);
}
}
class Sidebar extends React.Component {
constructor(props) {
this.handleSubmitButton = this.handleSubmitButton.bind(this);
}
handleSubmitButton(event) {
event.preventDefault();
// By giving the input the `ref` attribute, we can access it anywhere
const textInputValue = this.refs.input.value;
// Submit the value to the parent component
this.props.handleSubmitButton(textInputValue);
}
render() {
return (
<div>
<input type="text" ref="input" />
<button type="button" className="..." onClick={this.handleSubmitButton} >
<span className="glyphicon glyphicon-send" aria-hidden="true"/>
</button>
</div>
);
}
}
This prevents coupling your components together and will ease testing later.
In your child component you can create a property for the ref attribute. Accessing DOM elements directly is usually done by setting refs.
In your parent component you can use an arrow function with a callback which allows you to access the <input> DOM element anywhere within your parent component, by simply typing this.textInput
More information about refs can be found in the official React documentation: https://facebook.github.io/react/docs/refs-and-the-dom.html
Parent component:
handleSubmit() {
console.log(this.textInput.value);
}
<Sidebar
inputRef={(input) => this.textInput = input}
handleSubmitButton={this.state.handleSubmit}
/>
Child component:
<input ref={this.props.inputRef} type="text"/>
There's a couple ways you can go about it. One is you can add an onChange event to the input to update state of the Sidebar component. Then when the submit handler is clicked, pass the value from state, and have the HomePage handleSubmit() accept the value.
The other is to also pass an onChange prop from the HomePage component to the Sidebar. The input would then set the onChange to that prop.

Categories