Changing state of one component from another component in another file - javascript

I am new to React and web dev in general.
I have created a Component containing list of calculator like buttons which is stored in Buttons.js.
There is another component called as Submit stored in Submit.js. Submit component is basically a textbox in which we type a mathematical expression which I want to process later.
Both of these components are then called in another component call Leftwindow.js.
So my question is,
How can I make clicking in Buttons component affect the textbox in Submit component. I know it could be done easily had the buttons and input box been the part of a single component.
Basically if I press the '1' button I want it to be added to the input box.
A snapshot of how it looks -
Overview
Code for Buttons.js -
class Buttons extends Component {
constructor(props){
super(props);
this.state = {
//buttonrows//
};
}
render(){
const row1elems = this.state.row1.map((button) => {
return (
<Button color={colours[button.type]} className="buttonsize">{button.label}</Button>
);
});
const row2elems = this.state.row2.map((button) => {
return (
<Button color={colours[button.type]} className="buttonsize">{button.label}</Button>
);
});
const row3elems = this.state.row3.map((button) => {
return (
<Button color={colours[button.type]} className="buttonsize">{button.label}</Button>
);
});
const row4elems = this.state.row4.map((button) => {
return (
<Button color={colours[button.type]} className="buttonsize">{button.label}</Button>
);
});
return (
<div className="center">
<ButtonGroup>
{row1elems}
</ButtonGroup>
<ButtonGroup>
{row2elems}
</ButtonGroup>
<ButtonGroup>
{row3elems}
</ButtonGroup>
<ButtonGroup>
{row4elems}
</ButtonGroup>
</div>
);
}
}
export default Buttons;
Code for Submit.js -
class Submit extends Component{
constructor(props){
super(props);
this.state = {
fx: ''
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event){
const target = event.target;
const val = target.val;
const name = target.name;
this.setState({
[name]: val
})
}
handleSubmit(event){
}
render(){
return(
<div>
<Form onSubmit={this.handleSubmit}>
<FormGroup row>
<Col md={12}>
<Input type="text" id="fx" name="fx" placeholder="Type Here" value = {this.state.fx} onChange={this.handleInputChange} />
</Col>
</FormGroup>
</Form>
<Button type="submit" color="primary">Differentiate</Button>
</div>
)
}
}
export default Submit;
Code for LeftWindow.js --
import React, { Component } from 'react';
import Buttons from './Buttons';
import './Custom.css';
import Submit from './Submit';
class LeftWindow extends Component{
constructor(props){
super(props);
}
render(){
return(
<div className="col-3 bg-dark fullheight">
<Buttons/>
<h3 className="center">Enter the function to be differentiated</h3>
<Submit/>
</div>
);
}
}
export default LeftWindow;

This is how your common ancestor file will look like -
Added state having input.
Added a callback "handleInputChange" which updates this input state.
Passed this callback to Both the components.
In your Submit.js file you will need to change your input tag with this
<Input
type="text"
value={this.props.input}
onChange={e => {
this.props.handleInputChange(e.target.value);
}}
/>
Also in your Buttons.js file call this.props.handleInputChange on Button click.
<Button
onClick={() => {
this.props.handleInputChange(button.label)
}}
color={colours[button.type]}
className="buttonsize"
>
{button.label}
</Button>
That's it.
Hope I could help!

In React you work with tree of components where data can travel from top to bottom. It is possible to notify parent component about changed data via passed callbacks. If you have two components that have common ancestor, you can share data between them through this common ancestor.
Let's say you have component Parent which renders your Buttons and Submit components. If you store your data (or state) in Parent and pass this data as props with callbacks, your components then can notify Parent about things happened and parent can change it's state and pass new state as props to children.
There is a "state management" solutions when your data lives detached of your components and injected on one by one basis. In such you won't need parent to store the data, but if we talk about pure react - to share data between branches in react tree, this branches should have common ancestor somewhere in the tree.

Related

React handling Form Components

Alright I am trying to submit two different forms as independent components in another page
component where I only have one button to submit the data of both forms.
So I am struggling to have a shared state in the page component and I need to pass the whole state of each form component to my page component on submit.
Can anyone recommend a best practice for my use case ?
render() {
return (
<div as={Row} className="container" style={formStyle}>
<Col>
<Form onSubmit={this.submitData}>
<TripForm />
<PostForm heading="add your first blog entry" />
<Button variant="dark" type="submit">
Summing up
</Button>
</Form>
</Col>
</div>
);
}
define your state in the parent component and pass it down in props
class PageComponent = {
state = { } //define your state here
handleChange = () => {} // define a function that handles changing state
submitData = () => {
// in here you can access this.state and then submit form data with that state
}
render() {
return (
<div as={Row} className="container" style={formStyle}>
<Col>
<Form onSubmit={this.submitData}>
<TripForm handleChange={handleChange} someState={someState} />
<PostForm heading="add your first blog entry" handleChange={handleChange} someState={someState}/>
<Button variant="dark" type="submit">
Summing up
</Button>
</Form>
</Col>
</div>
);
}
}
I've also defined someState which you can pass down as props to the child/form components. once you set state in there with handleChange it will set state in the parent component and you can submitData with that state

REACT: Should HTML forms be Controlled or Uncontrolled components?

I am having a dilemma as to where Where My Form State Should Live.
React Docs mention:
Identify every component that renders something based on that state.
Find a common owner component (a single component above all the
components that need the state in the hierarchy). Either the common
owner or another component higher up in the hierarchy should own the
state. If you can’t find a component where it makes sense to own the
state, create a new component simply for holding the state and add it
somewhere in the hierarchy above the common owner component.
I have a simple comment UI.
The user enters a comment in the text area.
The user enters name in input field.
The user clicks post and the comment is displayed below.
Components Highrarchy is as follows:
- CommentSection.jsx ---> main (should "Post" state (?))
-comment-post.jsx ---> User Comment Form
- comment-table.jsx
- comment.jsx
- comment-avatar.jsx
- comment-body.jsx
Issue: In comment-post.jsx the input and textarea fields have onChange() event handler, and there is also a click event handle on submit post button. I can choose to do one of two:
In comment-post.jsx when onChange is trigger send the commentBody to CommentSection.jsx The issue here would be I will be sending the commentBody as soon as user types, then sending name later when it triggers and so on
In comment-post.jsx when onChange is trigger SAVE the value in state, then name the name field in the state, when user clicks submit button send to CommentSection.jsx Benefit is Fields are set first in state, and when the user clicks post they are sent to the parent (As it should be right?)
Should the input fields of the comment i.e. commentBody and Name be
saved in the comment-post.jsx (form component) or the parent?
Right now the state I am doing the 2. Saving form fields in state and then sending the state values on submit.
I think the issue is onChange and onClick are two different handlers, the question is which one should pass the values to parent component ideally?
class CommentSection extends Component {
state = {
posts: []
};
handleCommentPost = post => {
const posts = this.state.posts;
posts.push(post);
this.setState({ posts });
};
render() {
console.log("comment:", this.state.posts);
return (
<React.Fragment>
<h1>comments</h1>
<div className="row bootstrap snippets">
<div className="col-md-6 col-md-offset-2 col-sm-12">
<div className="comment-wrapper">
<Comment_Post onClick={this.handleCommentPost} />
<Comment_Table comments={this.state.posts} />
</div>
</div>
</div>
</React.Fragment>
);
}
}
class Comment_Table extends Component {
render() {
const posts = this.props.comments;
let count = 0;
return (
<div className="panel panel-info">
<hr />
<ul className="media-list">
{posts.map(post => (
<Comment key={count++} comment={post.commentBody} />
))}
</ul>
</div>
);
}
}
class Comment extends Component {
render() {
return (
<li className="media">
<Comment_Avatar userAvatar={this.props.commentAvatar} />
<Comment_Body userComment={this.props.comment} />
</li>
);
}
}
class Comment_Body extends Component {
render() {
const { userComment } = this.props;
return (
<div className="media-body">
<span className="text-muted pull-right">
<small className="text-muted">30 min ago</small>
</span>
<strong className="text-success">#MartinoMont</strong>
<p>
{userComment}
{}
</p>
</div>
);
}
}
class Comment_Post extends Component {
state = {
commentBody: null
};
onCommentChange = e => {
this.setState({ commentBody: e.target.value });
};
onCommentPost = e => {
const commentBody = this.state.commentBody;
if (commentBody !== null) {
this.props.onClick({ commentBody });
}
};
onNameInput = e => {};
onCommentPostError() {}
render() {
return (
<React.Fragment>
<div className="panel-heading p-heading">Comment panel</div>
<div className="panel-body">
<textarea
onChange={this.onCommentChange}
className="form-control"
placeholder="write a comment..."
rows="3"
/>
<label htmlFor="fname" onChange={this.onNameInput}>
Name:{" "}
</label>
<input id="fname" placeholder="John" />
<br />
<button
type="button"
className="btn btn-info pull-right"
onClick={this.onCommentPost}
>
Post
</button>
<div className="clearfix" />
</div>
</React.Fragment>
);
}
}
class Comment_Avatar extends Component {
render() {
return (
<a href="#" className="pull-left">
<img
src="https://bootdey.com/img/Content/user_1.jpg"
alt=""
className="img-circle"
/>
</a>
);
}
}
I think the issue is onChange and onClick are two different handlers, the question is which one should pass the values to parent component ideally?
There are two types of Forms that we use in standard User interface design. First is when any change in the Input saves the change. Second, when after making changes to the Form Elements, you press a submit button and then the changes are saved.
Since you have implemented the second type, your onChange should deal with controlling your TextArea state and your onClick should deal with the submit. So your code is just fine.
I am having a dilemma as to where Where My Form State Should Live.
That depends...In your case you only have one Form, and two Form Elements none of which are re-usable. So these uncontrolled Forms work well for you. However, if you wanted to have a Form Component that is re-usable, or if you wanted to have a Form with 15 Fields in it, you would not want to write a separate onChange handler for each one of them. For this, you would want to make a controlled Form Component that can handle all these things for you. Here's an example of what that would look like.
export default class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
values: {}
};
}
#boundMethod
handleSubmit(event) {
event.preventDefault();
this.props.submit(this.state.values);
}
#boundMethod
handleChange(event) {
const { name, value } = event.target;
const newValues = Object.assign(
{ ...this.state.values },
{ [name]: value }
);
this.setState({
values: newValues
});
}
public render() {
const { values } = this.state;
return (
<form onSubmit={this.handleSubmit} noValidate={true}>
<div>
{React.Children.map(
this.props.children,
child => (
{React.cloneElement(child, {
value: values[child.props.name],
onChange: this.handleChange
})}
)
)}
<div>
<button type="submit">
Submit
</button>
</div>
</div>
</form>
);
}
}
Then you will be able to use this Form class like so:
<Form
submit={values => {
/* work with values */
}}
>
<input type="hidden" name="name" />
<input type="hidden" name="rating" />
</Form>;

Cant get a text box state to refresh with user input, keeps going to default state despite using setState

I have a React file which displays a list of city data as a component. there is an input textbox above it which needs to accept user input. i am using state to display an initial string in the textbox, but i cannot get onChange to successfully use a function to setState. troubleshooting it with console.log i can see that when i attempt to change the state the function i am pointing to with onChange does work and changes one letter, but then the state snaps back to its default value. the problem seems to be with setState not saving the change and reverting back to the initial state after any changes are made. the text box content appears to not change at all, thought console.log shows a one letter change but then reverts back to the original state.
how do i update state? i want the user to be able to punch a number in and then compare it with the list.
import React, {Component} from 'react';
import Table from './Table';
import cities from './Cities';
class App extends Component {
state = {
userInput: "Your City Population"
}
popChanger = (event) => {
this.setState( {userInput: event.target.value} );
//console.log(event.target.value);
}
yourCity = (
<div>
<input
type='text'
onChange={this.popChanger}
value={this.state.userInput}
/>
</div>
)
render() {
return (
<div className = "App">
{this.yourCity}
<Table characterData = {cities} />
</div>
);
}
}
export default App;
setState() is saving your changes, just not in the right place,
popChanger() is an arrow function and updates the state of the App component,
yourCity has it's own this so it doesn't know about the App state.
you can either cahnge yourCity to an arrow function that returns the html you want like
class TodoApp extends React.Component {
state = {
a: ''
};
YourCity = () => (
<div>
<input type="text" onChange={this.handleChange} value={this.state.a} />
</div>
}
handleChange = e => this.setState({a : e.target.value})
render() {
return (
<div>
<this.YourCity />
</div>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
Or, create yourCity component outside and pass the handleChange as a prop :
const YourCity = props => (
<div>
<input type="text" onChange={props.handleChange} value={props.value} />
</div>
)
class TodoApp extends React.Component {
state = {
a: ''
};
handleChange = e => this.setState({a : e.target.value})
render() {
return (
<div>
<YourCity handleChange={this.handleChange} value={this.state.a}/>
</div>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
The state is updating but you can't see that because this.yourCity doesn't re-render
popChanger = (event) => {
this.setState( {userInput: event.target.value} );
console.log(event.target.value);
}
yourCity(){
return <div>
<input
type='text'
onChange={this.popChanger}
value={this.state.userInput}
/>
</div>
}
render() {
return (
<div className = "App">
{this.yourCity()}
</div>
);
}
}

How to call parent function in child component

I checked quite a lot of examples but found most of them having events fired in Child Component.
Can someone please suggest how can I call the Parent Component's function in child with the click event on Parent Component? Thanks.
Parent Component (app.js):
Class App extends Component {
handleClick = (e, id, text) => {
e.preventDefault();
this.setState({val: text})
}
render() {
return (
<div>
<Form val={this.state.val} Click={this.handleClick.bind(this) }/>
<Button onClick={(e) => this.handleClick(e, todo.id, todo.text)}>
<Icon>edit_icon</Icon>
</Button>
</div>
)
}
}
Child Component (form.js):
this.props.Click(); //where should i call this function since my button is in parent component
Class Form extends Component{
render() {
const { text } = this.state;
return (
<TextField
value={text}
color="secondary"
/>
)
}
}
}
If you want to call it in your Child component, you need an event to trigger it or maybe a condition. So, for example in your form.js, we will trigger it with a button click form your child component
render() {
const { text } = this.state;
return (
<TextField
value={text}
color="secondary"
/>
<Button onClick={this.props.Click} />
)
}
}
Maybe, using a Button in your child component is not a great choice for your case since you already have a Button to call the Click function in your parent component, this Button in child component I made is only for example
One way you can do this is use a ref to call the function..
// create the ref
constructor() {
super();
this.myFormRef = React.createRef()
}
// click handler for the button
handleClick = (e, id, text) => {
// here you have the child instance which gives you access to its functions
this.myFormRef.someChildMethodThatIsOnTheChildClass()
}
render() {
// notice here we use the ref on the form via... ref={this.myFormRef}
return (
<Form val={this.state.val} ref={this.myFormRef} Click={this.handleClick.bind(this) }/>
<Button onClick={(e) => this.handleClick(e, todo.id, todo.text)}>
<Icon>edit_icon</Icon>
</Button>
)
)
I would like to note though that it doesn't seem to make much sense as to why you want to do this. You should probably re-think your architecture. Also what is the button press supposed to be doing? submitting the form?

React changing parent state during input onChange getting stuck

I am building a huge form which is made of atleast 50 inputs.
I have wrote a function in the form component that will save the value of the input to the form state:
PARENT FUNCTION
saveToState(details) {
const { company } = this.state;
company[details.part][details.element] = details.value;
this.setState({ company });
}
PASSING TO CHILD COMPONENT (INPUT)
<FieldInput
label="Name (as shown) *"
part="information"
element="displayName"
saveToState={this.saveToState}
/>
Here is the Input component:
import React, { Component } from 'react';
export default class FieldInput extends Component {
render() {
const { label, part, element, saveToState } = this.props;
return (
<div className="field">
<label>{label}</label>
<div className="ui input">
<input
type="text"
name={`${part}[${element}]`}
onChange={(e) => saveToState({
part,
element,
value: e.target.value
})}
/>
</div>
</div>
);
}
}
In result whenever I type something in the input It's taking it 200-300ms to really display what I wrote in the input, the state is getting updates instantly but whenever I type a character I set the new state of the parent form and update it which updates the whole component. The only way i found around it is to use saveToState within the parent component without passing it down. but that would require 1000's of line of code, Is there any way around this? Thanks!
There are alot of ways you can solve this problem. The easiest one and the fastest one is to use onBlur instead of onChange that way setState will happen not when you key pressing in the input but when the input loses focus.
import React, { Component } from 'react';
export default class FieldInput extends Component {
render() {
const { label, part, element, saveToState } = this.props;
return (
<div className="field">
<label>{label}</label>
<div className="ui input">
<input
type="text"
name={`${part}[${element}]`}
onBlur={(e) => saveToState({
part,
element,
value: e.target.value
})}
/>
</div>
</div>
);
}
}

Categories