not able to pass value to parent component react - javascript

I am not able to pass the value from the child to parent component. I am new to react please guide me.
This is Greeting component -
/* eslint-disable react/prop-types */
import React from "react";
const Greeting = props => {
const isLoggedIn = props.isLoggedIn;
const name = props.name;
if (isLoggedIn) {
return <h1> Welcome Back {name}</h1>;
} else {
return <LoginInfo name={name} onChange={props.onChange} />;
}
};
function LoginInfo(props) {
return (
<div>
<h1> Please Login</h1>
<input type="text" value={props.name} onChange={props.onChange} />
</div>
);
}
export default Greeting;
This is login component -
import React, { Component } from "react";
import Greeting from "./Greeting";
import LogoutButton from "./LogoutButton";
import LoginButton from "./LoginButton";
class Login extends Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {
isLoggedIn: false,
name: ""
};
}
handleLoginClick() {
this.setState({ isLoggedIn: true });
}
handleLogoutClick() {
this.setState({ isLoggedIn: false });
}
onChange = e => {
this.setState({
name: e.target.value
});
};
render() {
const isLoggedIn = this.state.isLoggedIn;
const name = this.state.name;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting
isLoggedIn={isLoggedIn}
name={name}
onChange={this.onChange}
/>
{button}
</div>
);
}
}
export default Login;
I am using in my app.
<Login />
In greeting component How whatever user entered i can store in state and display in welcome back line with name.

I think you need to maintain state in your parent component and emit change event from child component and change state in parent.
like
In login component
nameChange = (e) => {
this.setState({
name: e.target.value
})
}
<Greeting isLoggedIn={isLoggedIn} name={this.state.name} nameChange={this.nameChange}/>
and in child
<input
type="text"
name="name"
value={this.props.name}
onChange={this.props.nameChange}
/>

So this is how I would refactor your code for the LogInInfo component to pass the values of the user to your LogIn component
First of all you need a state in LogInInfo since you want to store the values of the user, you will use the state for that
/* eslint-disable react/prop-types */
import React from "react";
const Greeting = props => {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <h1> Welcome Back</h1>;
} else {
return <LoginInfo submitForm={props.handleSubmitForm}/>;
}
};
class LoginInfo extends React.Component {
constructor(props) {
this.state = {
name: ""
}
}
handleChange = (event) => {
this.setState({ name: event.target.value })
}
render(){
return (
<div>
<h1> Please Login</h1>
// e is the event that is passed automatically to the function in onSubmit
<form onSubmit={e => this.props.submitForm(e, this.state.name)}>
<input
type="text"
name="name"
value={name}
onChange={this.handleChange}
/>
<input type="submit" />
</form>
</div>
);
}
}
export default Greeting;
At this point you have the value you want to pass to the parent component LogIn to pass this value up, you need a function in your parent component that would get this value for you and pass it to the child component so it can be used, we will do this via props. We also need a variable in the parent's state that will store this value
import React, { Component } from "react";
import Greeting from "./Greeting";
import LogoutButton from "./LogoutButton";
import LoginButton from "./LoginButton";
class Login extends Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {
isLoggedIn: false,
username: ""
};
}
handleLoginClick() {
this.setState({ isLoggedIn: true });
}
handleLogoutClick() {
this.setState({ isLoggedIn: false });
}
// when you use arrow functions they automatically bind to your component
handleSubmitForm = (e, value) => {
// the following line prevents the page from refreshing when you submit any form
e.preventDefault()
this.setState({ username: value })
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} submitForm={this.submitForm}/>
{button}
</div>
);
}
}
export default Login;
when you click the submit button it should update the state in the LogIn component. Hope this helps

You need to do 2 things:
create a function which will handle changes of the input in Login component, send it to Greeting and then to LoginInfo. the function would look something like:
onChange = (event) => this.setState({event.target.name: event.target.value})
note: event.target.name equals to name prop of input. the function above will work only if the name of the state that stores that input value equals to the input name, in your case name.
send the state that stores the value of the input to the LoginInfo. the input element should look like this:
<input type="text" name="name" value={valueFromLogin} onChange={props.onChangeFromLogin} />

Related

React form sending empty object

I am trying to send user input in a form component to the component managing the state. I tried to use a callback but my input is not being sent.
Tried to use the form object in a callback
//here is the form component
class ListForm extends React.Component {
constructor(props) {
super(props);
this.state = {
NewItem: {
itemText: "",
id: Date.now(),
completed: false
}
};
}
handleChanges = e => {
this.setState({...this.state,
NewItem: { ...this.state.NewItem, [e.target.name]: e.target.value }
});
};
submitItem = e => {
this.setState({ itemText: "" });
this.props.addItem(e, this.state.NewItem);
};
render() {
return (
<form onSubmit={this.submitItem}>
<input
type="text"
value={this.state.NewItem.itemText}
name="itemText"
onChange={this.handleChanges}
/>
<button>Add</button>
</form>
);
}
}
export default ListForm;
addItem is in the parent component and looks like this. Groceries is just an array of objects with itemText, id and completed
addItem = (e, item) => {
e.preventDefault();
this.setState({
...this.state,
groceries: [...this.state.groceries, item]
});
};
I can enter in the form, but when I hit enter, nothing is added to the list
Very first thing to try is to add e.persist to your addItem() function
addItem = (e, item) => {
e.preventDefault();
e.persist()
this.setState({
...this.state,
groceries: [...this.state.groceries, item]
});
};
Another option is using redux to save the form data in the Global state and then accessing it in the parent component through redux. Here is an example of what that might look like:
import React, { Component } from 'react';
import * as ACTIONS from '../store/actions/actions';
import { connect } from 'react-redux';
class Form1 extends Component {
state ={
value: ''
}
handleChange = (event) => (
this.setState({value: event.target.value})
)
handleSubmit = (event) => {
event.preventDefault()
this.props.input_action_creator(event.target.name.value)
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input id="name" onChange={this.handleChange} type="text" />
<button type="submit"> Submit </button>
</form>
<br />
</div>
)}
}
function mapDispatchToProps(dispatch) {
return {
input_action_creator: (text) => dispatch(ACTIONS.user_input(text))
}
}
export default connect(mapDispatchToProps)(Form1);
class Parent extends Component {
access form data with
this.props.user_Text
}
function mapStateToProps(state) {
return {
user_text: state.user_reducer.user_text
}
}
here is a fully funcitoning react-redux project if you are interested
https://github.com/iqbal125/modern-react-app-sample

Why is my component not rerendering after form submission and setting state with Firebase?

I am building a component that is supposed to rerender after a form submission, and it sends the database perfectly fine, but I want the component to rerender after it submits and not having to refresh the page.
I have heard React rerenders after state is changed, and I have tried to set the state of the form inputs on submit.
import React, { Component } from 'react';
import { withFirebase } from '../Firebase';
import { FirebaseContext } from '../Firebase';
import { Link } from 'react-router-dom';
import AddNew from '../AddNew';
import { compose } from 'recompose';
import Firebase from '../Firebase';
import * as ROUTES from '../../constants/routes';
import { throwStatement, thisExpression, tsExpressionWithTypeArguments } from '#babel/types';
class Home extends Component {
constructor(props) {
super(props)
this.state = {
loading: false,
isHidden: false,
name: '',
image: '',
data: []
}
this.baseState = this.state
this.toggleAddNew = this.toggleAddNew.bind(this);
}
getPosts() {
this.props.firebase.getClients().then(snapshot => {
this.setState({
data: snapshot.docs
})
});
}
// Component lifecycle methods
componentWillMount() {
this.getPosts()
}
componentDidUpdate(){
console.log('updated')
}
toggleAddNew() {
this.setState({
isHidden: !this.state.isHidden
})
}
updateInput = e => {
this.setState({
[e.target.name]: e.target.value
});
}
resetForm = () => {
this.setState(this.baseState)
}
deletePost = (id) => {
this.props.firebase.deleteClient(id);
}
addClient = e => {
e.preventDefault();
this.props.firebase.addClient().add({
name: this.state.name,
image: this.state.image
})
this.setState({
name: '',
image: ''
});
this.resetForm();
};
render() {
const renderPosts = this.state.data.map((item) => (
<li data-id={item.id} className="client-wrapper col-sm-4">
<button onClick={() => this.deletePost(item.id)}>X</button>
<Link to={`/dates/${item.id}`}>
<h2>{item.data().name}</h2>
</Link>
<Link to={`/dates/${item.id}`}>
<img src={item.data().image} />
</Link>
</li>
));
return (
<div>
<ul id="client-list" className="row">{renderPosts}</ul>
<button onClick={this.toggleAddNew.bind(this)}>Add New</button>
{this.state.isHidden ?
<div id="add-new-form-wrapper">
<button onClick={this.toggleAddNew.bind(this)} id="x-add-new">X</button>
<form onSubmit={this.addClient.bind(this)} id="add-new-form">
<input type="text" name="name" placeholder="Name" onChange={this.updateInput} value={this.state.name} />
<input type="text" name="image" placeholder="Image" onChange={this.updateInput} value={this.state.image} />
<button type="submit">Submit</button>
</form>
</div> :
''}
</div>
)
}
}
export default compose(
withFirebase,
)(Home);
This
this.baseState = this.state
only makes a copy of object reference, not a copy of state object (with property values).
When we have a reference copy
resetForm = () => {
this.setState(this.baseState)
}
can work like state = state, does nothing.
The copy of object (with current property values) can be done (f.e.) this way:
this.baseState = {...this.state}
With this small fix it should work ...
... if not, try
resetForm = () => {
this.setState({...this.baseState})
}
You can also update some state field with current time to force rerender or simply call this.forceUpdate() (see docs).
BTW - resetForm shouldn't overwrite data. Luckily we have a copy of data object reference in baseState ;)

How do I display only the text user has written from component to another?

After I'm done creating an article via CreateArticle component (which works fine), I want to display only what the user has written (its value) in <textarea value={value}> in the SearchArticle component via displayName() function.
In other words, in CreateArticle component when the user's done typing something in <textarea/> followed by clicking Submit (which's a button that saves what the user wrote), I want to only display what the user has written in Search Article inside displayName() function.
What am I doing wrong and how can I fix it?
Here's CreateArticle:
import React, { Component } from 'react';
import {connect} from "react-redux";
import * as actionType from "../../store/actions/actions";
class CreateArticle extends Component {
constructor(props) {
super(props);
}
handleSubmit = event => {
this.setState({storyTextValue: event.target.value});
this.props.storyTextValueRedux(event.target.storyTextValue);
event.preventDefault();
}
handleStoryText = event => {
event.preventDefault();
this.setState({value: event.target.value});
}
onSubmit = () => {
if(this.state.value === "") {
alert("Please enter the value and then click submit");
} else {
alert("Article saved " + '\n' + this.state.value + this.props.articleIdValue);
}
}
render() {
return(
<div>
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.props.cityCodeValue} type="text" placeholder="city code"/>
<input type="text" placeholder="author name"/>
<textarea value={this.props.storyTextValue} onChange={this.handleStoryText} rows="2" cols="25" />
<button type="submit" value="Submit" onClick={() => this.onSubmit()}>Submit</button>
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
articleIdValue: state.articleIdValue.articleIdValue,
storyTextValue: state.storyTextValue.storyTextValue
};
};
const mapDispatchToProps = dispatch => {
return {
articleIdValueRedux: (value) => dispatch({type: actionType.ARTICLE_ID_VALUE, value}),
storyTextValueRedux: (value) => dispatch({type: actionType.STORY_VALUE, value})
};
};
export default connect(mapStateToProps, mapDispatchToProps)(CreateArticle);
Here's SearchArticle:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actionType from '../../store/actions/actions';
import ArticleText from '../../containers/ArticleText/ArticleText';
class SearchArticle extends Component {
constructor(props) {
super(props);
this.state = {
flag: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
this.props.CityCodeReducerRedux(event.target.value);
}
handleSubmit(event) {
this.setState({flag: true});
event.preventDefault();
}
displayName = () => {
if(this.props.cityCodeValue === "nyc" || this.props.articleIdValue === 1) {
return(
<div>
<p>author name: {this.props.authorNameValue}</p>
<p>article text: {<ArticleText/>}</p>
</div>
);
}
}
render() {
return(
<div>
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.props.cityCodeValue} type="text" placeholder="city code"/>
<input onChange={this.handleChange} value={this.props.articleIdValue} placeholder="article id"/>
<button onClick={() => this.displayName} value="Search">Submit</button>
{this.state.flag ? this.displayName() : null}
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
cityCodeValue: state.cityCodeValue.cityCodeValue,
authorNameValue: state.authorNameValue.authorNameValue,
articleIdValue: state.articleIdValue.articleIdValue
};
};
const mapDispatchToProps = dispatch => {
return {
CityCodeReducerRedux: (value) => dispatch({type: actionType.CITY_CODE_VALUE, value}),
articleIdValueRedux: (value) => dispatch({type: actionType.ARTICLE_ID_VALUE, value})
};
};
export default connect(mapStateToProps, mapDispatchToProps)(SearchArticle);
Here's StoryTextReducer:
import * as actionType from '../store/actions/actions';
const initialState = {
storyTextValue: ''
};
const StoryTextReducer = (state = initialState, action) => {
switch (action.type) {
case actionType.STORY_VALUE:
return {
...state,
storyTextValue: action.value
};
default:
return state;
}
};
export default StoryTextReducer;
You're looking at 2 different instances of <ArticleText>. Changing properties of the instance in CreateArticle will not change the instance in SearchArticle
Instead of trying to share a value by using the same class, you should be sharing a value by whats in the redux store.
You should define an action that dispatches and event like {type: updateArticleText, articleText: foo}, then in your reducer you can set the redux state to have a property ArticleText equal to event.articleText.
Now that your value is stored in redux store rather than component state, your components simply get props from the redux store in the mapStateToProps function.

React: How can i run a function from another Component with state Variables

I have this free components:
my LoginForm:
import React, { Component } from "react";
import { Pane, TextInputField, Checkbox, Button } from "evergreen-ui";
import { validateEmail, validatePassword } from "./FormValidator";
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
passwordErr: {
status: false,
value: ""
},
emailErr: {
status: false,
value: ""
},
email: "",
password: "",
CheckBoxchecked: false
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleCheckbox = this.handleCheckbox.bind(this);
}
handleEmailInput = e => {
const email = e.target.value;
this.setState({ email: email });
};
handlePasswordInput = e => {
const password = e.target.value;
this.setState({ password: password });
};
handleCheckbox() {
this.setState({
CheckBoxchecked: !this.state.CheckBoxchecked
});
}
handleSubmit() {
if (this.checkFormStatus()) {
alert("Form OK");
}
}
checkFormStatus() {
// form validation middleware
const { email, password } = this.state;
const emailErr = validateEmail(email);
const passwordErr = validatePassword(password);
if (!emailErr.status && !passwordErr.status) {
return true;
} else {
this.setState({
emailErr,
passwordErr
});
return false;
}
}
render() {
return (
<Pane padding={15}>
<TextInputField
tabIndex={0}
required
isInvalid={this.state.emailErr.status}
validationMessage={
this.state.emailErr.status ? this.state.emailErr.value : false
}
onChange={this.handleEmailInput}
value={this.state.email}
appearance="neutral"
type="email"
label="Your email-address"
inputHeight={36}
//description="We’ll need your email-address to create an new account"
/>
<TextInputField
required
validationMessage={
this.state.passwordErr.status ? this.state.passwordErr.value : false
}
isInvalid={this.state.passwordErr.status}
onChange={this.handlePasswordInput}
value={this.state.password}
appearance="neutral"
label="Your Password"
type="password"
inputHeight={36}
//description="Create a secure password to protect your account"
/>
<Checkbox
label="Keep me logged in"
checked={this.state.CheckBoxchecked}
onChange={this.handleCheckbox}
/>
</Pane>
);
}
}
export default LoginForm;
my export LoginFormButton:
export class LoginFormButton extends Component {
render() {
return (
<Button
appearance="primary"
marginLeft={8}
marginRight={16}
intent="success"
onClick={} //How can i call handleSubmit() from here?
>
Login
</Button>
);
}
}
and my Dialog...
import ReactDOM from "react-dom";
import LoginForm from './LoginForm';
import LoginFormButton from './LoginFormButton';
class LoginDialog extends Components {
render(
return(
<Dialog>
<LoginForm/>
<div className="Footer">
<LoginFormButton/>
</div>
</Dialog>
);
)
}
I have the function handleSubmit() stored in the LoginForm Component. I want to call these function from the LoginFormButton. This Button is contained in the Dialog Component:
How can i do this? Thanks for your answer and our help. I am a beginner, so i don't exactly know, how can i implement this.
Looks like, you want to have a LoginForm Component, and a LoginFormButton to handle form submit.
import React, { Component } from "react";
import { Pane, TextInputField, Checkbox, Button } from "evergreen-ui";
import { validateEmail, validatePassword } from "./FormValidator";
const LoginFormButton = ({ handleSubmit }) => {
return (
<Button
appearance="primary"
marginLeft={8}
marginRight={16}
intent="success"
onClick={() => handleSubmit()} // or just -> onClick={handleSubmit}
>
Login
</Button>
);
}
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
passwordErr: {
status: false,
value: ""
},
emailErr: {
status: false,
value: ""
},
email: "",
password: "",
CheckBoxchecked: false
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleCheckbox = this.handleCheckbox.bind(this);
}
handleEmailInput = e => {
const email = e.target.value;
this.setState({ email: email });
};
handlePasswordInput = e => {
const password = e.target.value;
this.setState({ password: password });
};
handleCheckbox() {
this.setState({
CheckBoxchecked: !this.state.CheckBoxchecked
});
}
handleSubmit() {
if (this.checkFormStatus()) {
alert("Form OK");
}
}
checkFormStatus() {
// form validation middleware
const { email, password } = this.state;
const emailErr = validateEmail(email);
const passwordErr = validatePassword(password);
if (!emailErr.status && !passwordErr.status) {
return true;
} else {
this.setState({
emailErr,
passwordErr
});
return false;
}
}
render() {
return (
<Pane padding={15}>
<TextInputField
tabIndex={0}
required
isInvalid={this.state.emailErr.status}
validationMessage={
this.state.emailErr.status ? this.state.emailErr.value : false
}
onChange={this.handleEmailInput}
value={this.state.email}
appearance="neutral"
type="email"
label="Your email-address"
inputHeight={36}
//description="We’ll need your email-address to create an new account"
/>
<TextInputField
required
validationMessage={
this.state.passwordErr.status ? this.state.passwordErr.value : false
}
isInvalid={this.state.passwordErr.status}
onChange={this.handlePasswordInput}
value={this.state.password}
appearance="neutral"
label="Your Password"
type="password"
inputHeight={36}
//description="Create a secure password to protect your account"
/>
<Checkbox
label="Keep me logged in"
checked={this.state.CheckBoxchecked}
onChange={this.handleCheckbox}
/>
<LoginFormButton
handleSubmit={this.handleSubmit}
/>
</Pane>
);
}
}
export default LoginForm;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
1) Validation will happen in LoginForm Component, unless you pass this.state.password and this.state.email to LoginFormButton Component. I wouldn't advice to do that, as validation can be handled in LoginForm Component.
2) HandleSubmit function can be passed to LoginFormButton component as props from LoginForm component, and can be directly used within the component.
I would advice to follow JS standards for naming conventions(camelCases) in your state variable. And use Arrow Functions so that you don't have to bind function explicitly, unless otherwise.
I Changed you LoginFormButton to a stateless Component, as it does not require a state. It is better that way, and what React Community encourages.
Check below corrected code
LoginForm component
import React, { Component } from "react";
import { Pane, TextInputField, Checkbox, Button } from "evergreen-ui";
import { validateEmail, validatePassword } from "./FormValidator";
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
passwordErr: {
status: false,
value: ""
},
emailErr: {
status: false,
value: ""
},
email: "",
password: "",
CheckBoxchecked: false
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleCheckbox = this.handleCheckbox.bind(this);
}
handleEmailInput = e => {
const email = e.target.value;
this.setState({ email: email });
};
handlePasswordInput = e => {
const password = e.target.value;
this.setState({ password: password });
};
handleCheckbox() {
this.setState({
CheckBoxchecked: !this.state.CheckBoxchecked
});
}
handleSubmit() {
if (this.checkFormStatus()) {
alert("Form OK");
}
}
checkFormStatus() {
// form validation middleware
const { email, password } = this.state;
const emailErr = validateEmail(email);
const passwordErr = validatePassword(password);
if (!emailErr.status && !passwordErr.status) {
return true;
} else {
this.setState({
emailErr,
passwordErr
});
return false;
}
}
render() {
return (
<Pane padding={15}>
<TextInputField
tabIndex={0}
required
isInvalid={this.state.emailErr.status}
validationMessage={
this.state.emailErr.status ? this.state.emailErr.value : false
}
onChange={this.handleEmailInput}
value={this.state.email}
appearance="neutral"
type="email"
label="Your email-address"
inputHeight={36}
//description="We’ll need your email-address to create an new account"
/>
<TextInputField
required
validationMessage={
this.state.passwordErr.status ? this.state.passwordErr.value : false
}
isInvalid={this.state.passwordErr.status}
onChange={this.handlePasswordInput}
value={this.state.password}
appearance="neutral"
label="Your Password"
type="password"
inputHeight={36}
//description="Create a secure password to protect your account"
/>
<Checkbox
label="Keep me logged in"
checked={this.state.CheckBoxchecked}
onChange={this.handleCheckbox}
/>
<div className="Footer">
<LoginFormButton handleSubmit={this.handleSubmit} />
</div>
</Pane>
);
}
}
export default LoginForm;
LoginFormButton component
export class LoginFormButton extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<div>
<Button
appearance="primary"
marginLeft={8}
marginRight={16}
intent="success"
onClick={this.props.handleSubmit}
>
Login
</Button>
</div>
);
}
}
Dialog component
import ReactDOM from "react-dom";
import LoginForm from './LoginForm';
import LoginFormButton from './LoginFormButton';
class LoginDialog extends Components {
render(
return(
<Dialog>
<LoginForm/>
</Dialog>
);
)
}
I recommend that you do not put the business logic in the button component. The button's only job should be to notify whoever's using it that the button has been clicked. This is done by passing a prop to the button:
export class LoginFormButton extends Component {
render() {
return (
<Button
appearance="primary"
marginLeft={8}
marginRight={16}
intent="success"
onClick={this.props.onClick}
>
Login
</Button>
);
}
}
Then whatever component uses the prop will be where the business logic happens. The other solutions assumed the login form button would be part of the login form, and that's probably how i would do it as well. So my recommendation is to take this part out of LoginDialog:
<div className="Footer">
<LoginFormButton/>
</div>
Move it into LoginForm.js, and add the callback:
render() {
return (
<Pane padding={15}>
{/* inputs and checkbox components omitted */}
<div className="Footer" onClick={this.handleSubmit}>
<LoginFormButton/>
</div>
</Pane>
)
}
If you can't do move the button into the form for some reason, then you'll have more work to do. You'll want to move the business logic up to the component which is the shared ancestor of both the button and the text inputs -- ie, LoginDialog. So you'd take most of the code that's currently in LoginForm and move it to LoginDialog instead. This includes moving the state up from LoginForm to LoginDialog. LoginForm will then need callbacks to pass relevant changes back to the loginDialog, so that the dialog can update its state. An when the button is pressed, that event will go to handleSubmit in LoginDialog.
This act of moving state up to a parent is fairly common in react development, enough so that it's mentioned in react's tutorial page here: https://reactjs.org/tutorial/tutorial.html#lifting-state-up . So it's a valid way to solve the problem, but in this case i think you're probably better off just moving the button into the form

Clear input in stateless React component

I want to implement an X icon inside Input component that will clear the input field. I can easily do it if I control the state. But is it actually possible with stateless component?
I use react-semantic-ui, their stateful components have auto controlled state.
So I want to create an input that can be used like this:
//Controlled
class App extends React.Component {
state = {
value:''
}
onChange = (event, props) => {
this.setState({value: props.value});
}
onClearInput = () => {
this.setState({value: ''});
}
render() {
return (
<MyInput
clearable
value={this.state.value}
onChange={this.onChange}
onClearInput={this.onClearInput}
/>
)
}
}
Or
// Uncontrolled
class App extends React.Component {
onChange = (event, props) => {
doSomething(props.value);
}
render() {
return (
<MyInput
clearable
onChange={this.onChange}
/>
)
}
}
In the second example, clearable feature will not work because we're not controlling the value.
MyInput can be implemented like this:
import React from 'react';
import { Input } from 'semantic-ui-react';
import ClearIcon from './ClearIcon';
function MyInput(props) {
const prepareProps = {...props};
if (props.clearable) {
prepareProps.icon=<ClearIcon onClick={props.onClearInput} />;
delete prepareProps.clearable;
}
delete prepareProps.onClearInput;
return (
<div className="my-input">
<Input {...prepareProps} />
</div>
);
}
...etc.
My problems:
clearable feature must work in both controlled and uncontrolled manner.
clearable feature should not require a handler. It would be nice to just provide a prop and handle the render and behavior of the X button under the hood.
I don't see any way to make this work. Any ideas?
Allowing the user of your component to set the value via props and still being able to clear the input can be easily achieved, e.g. like this:
class MyInput extends React.Component {
constructor(props) {
super(props);
this.state = {value: props.value || ''};
}
handleChange = event => {
const { onChange } = this.props;
this.setState({ value: event.currentTarget.value });
onChange && onChange(event);
};
handleClear = () => {
const { onClearInput } = this.props;
this.setState({ value: "" });
onClearInput && onClearInput();
};
render() {
const { value } = this.state;
const { clearable, onChange, ...inputProps } = this.props;
const clearIcon = clearable && <ClearIcon onClick={this.handleClear} />;
return (
<div className="my-input">
<Input value={value} icon={clearIcon} onChange={this.handleChange} {...inputProps} />
</div>
);
}
}
You could even make it more composable by using an hoc or render props as proposed by #pkuzhel.
Look at this codesandbox example to see it in action.
#Andrey
Would you try this below code? and let me know if that resolves your issue.
import React, { Component } from 'react';
import { Input, Button } from 'semantic-ui-react'
class App extends Component {
clear = () => {
console.log(this.inputRef.target.value);
this.inputRef.target.value = '';
}
render() {
return (
<div className="App">
<Input placeholder='Search...' onChange={(input) => {input.persist(); this.inputRef = input}} />
<Button onClick={this.clear}>Clear</Button>
</div>
);
}
}

Categories

Resources