reactjs - communication between two components - javascript

I want to call a function from one component to another. I've tried a few different things after googling, but as I'm new to reactjs I can't seem to work it out. I thought I could import the state and then change it from the other component (see below) but I think I'm barking up the wrong tree?
I want to call showMenu() in header.js from locationform.js
header.js
import React, { Component, PropTypes } from 'react';
import ReactF1 from 'react-f1';
// Styles.
import styles from './Header.css';
// Elements.
import Menu from './elements/Menu';
// Menu states.
import { states, SHOWBUTTON, IDLE, HIDE, OUT } from './elements/Menu/states';
import transitions from './elements/Menu/transitions';
function changeOffset() {
document.querySelector('svg path').style.strokeDashoffset = 0;
setTimeout("document.querySelector('svg path').style.strokeDashoffset = 171", 2000);
}
export default class Header extends Component {
static get propTypes() {
return {
children: PropTypes.element,
};
}
constructor(props, context) {
super(props, context);
this.state = {
menuState: OUT,
hamburgerState: OUT,
};
}
componentDidMount() {
if (this.props.router.location.pathname === '/') {
setTimeout(() => this.setState({ hamburgerState: SHOWBUTTON }), 6000);
} else {
this.setState({ hamburgerState: SHOWBUTTON });
}
// setTimeout(changeOffset, 8000);
}
completeF1Handler() {}
showMenu() {
this.setState({ menuState: IDLE });
}
hideMenu() {
this.setState({ menuState: HIDE });
}
reset() {
this.setState({ menuState: OUT });
}
render() {
const { stageWidth, stageHeight } = this.props;
return (
<ReactF1
className={styles.Header}
go={this.state.hamburgerState}
states={states(stageWidth, stageHeight)}
transitions={transitions()}
onComplete={() => this.completeF1Handler()}
>
<svg className={styles.hamburger} data-f1="hamburger" width="50" height="50">
<path
className={styles.triangle}
d="M0 0 L50 0 L0 50 Z"
onClick={this.showMenu.bind(this)}
fill={this.props.menuColor}
/>
</svg>
<Menu
go={this.state.menuState}
close={this.hideMenu.bind(this)}
reset={this.reset.bind(this)}
/>
</ReactF1>
);
}
}
locationform.js
import React, { Component, PropTypes } from 'react';
import { isDesktop } from '../../utils/device';
import LocateSelect from '../LocateSelect';
import styles from './LocationForm.css';
import buttonStyles from '../Button/Button.css';
// Menu states.
import { states, SHOWBUTTON, IDLE, HIDE, OUT } from '../Header/elements/Menu/states';
export default class LocationForm extends Component {
static get propTypes() {
return {
zipCode: PropTypes.string,
searchRadius: PropTypes.string,
businessType: PropTypes.string,
handleChange: PropTypes.func,
handleGeolocate: PropTypes.func,
handleSubmit: PropTypes.func,
};
}
constructor(props, context) {
super(props, context);
}
showMenu() {
console.log("show menu");
this.setState({ menuState: IDLE });
}
renderSelect() {
const { searchRadius, businessType, handleChange } = this.props;
return (
<div className={styles.selectContainer}>
<LocateSelect
id="searchRadius"
defaultValue=""
value={searchRadius}
handleChange={handleChange}
options={[
{
value: '',
text: 'SEARCH RADIUS',
},
{
value: '1',
text: '1 MI',
},
{
value: '5',
text: '5 MI',
},
{
value: '10',
text: '10 MI',
},
{
value: '25',
text: '25 MI',
},
]}
/>
<LocateSelect
id="businessType"
defaultValue=""
value={businessType}
handleChange={handleChange}
options={[
{
value: '',
text: 'BUSINESS TYPE',
},
{
value: 'bar',
text: 'Bar',
},
{
value: 'restaurant',
text: 'Restaurant',
},
{
value: 'liquorstore',
text: 'Liquor Store',
},
]}
/>
</div>
);
}
render() {
const {
zipCode,
handleChange,
handleSubmit,
handleGeolocate,
handleFocus,
handleBlur,
} = this.props;
return (
<form className={styles.LocationForm} onSubmit={handleSubmit}>
<input
type="button"
className={`${buttonStyles.Button} ${buttonStyles.dark} ${styles.geolocate}`}
value="Use Current Location"
onClick={handleGeolocate}
/>
<p>OR</p>
<input
id="zipCode"
type="text"
placeholder="ZIP CODE"
value={zipCode}
maxLength="5"
pattern="[0-9]*"
onFocus={handleFocus}
onBlur={handleBlur}
onChange={event => handleChange(event.target.id, event.target.value)}
/>
{this.renderSelect()}
<input
className={`${buttonStyles.Button} ${buttonStyles.dark}`}
type="submit"
value="search"
/>
<div className={buttonStyles.Button} onClick={() => this.showMenu()}>
No
</div>
</form>
);
}
}

With considering parent child communication in react you have to write a parent for both of these files and passing the function and the state as props .
For example :
app.js
import Header from './header.js'
import LocationForm from './location-form.js'
export default class App extends Component {
state = {menuState : 'out'}
showMenu() {
this.setState({ menuState:'idle' });
}
render(){
return(
<div>
<Header showMenu={this.showMenu} menuState={this.state.menuState}/>
<LocationForm showMenu={this.showMenu} menuState={this.state.menuState}/>
</div>
)
}
}
I think this concept of thinking will help you to reach the answer

So in react in order to pass data from parent to child elements you need to pass them down as props
So using your Menu component as an example you would pass the methods you want to use down to that child component like this: (I would recommend converting those to es6 syntax so you don't have to bother with binding).
//METHODS SECTION
const showMenu = () => {
this.setState({ menuState: IDLE });
}
const hideMenu = () => {
this.setState({ menuState: HIDE });
}
const reset = () => {
this.setState({ menuState: OUT });
}
//IN YOUR RENDER METHOD
<Menu
go={this.state.menuState}
close={this.hideMenu.bind(this)}
reset={this.reset.bind(this)}
showMenu={this.showMenu}
hideMenu={this.hideMenu}
reset={this.reset}
/>
Then in your child component you would refer to that method as this.props.showMenu or this.props.hideMenu or this.props.reset:
onClick(() => {this.props.showMenu()})
If you want the method to fire on an event you should wrap it in an anonymous function like I did above. You can also add it to another method within the child component that will get called when that method is called so:
const doSomething = () => {
count++
this.props.showMenu()
}
onClick(() => {this.doSomething()})
When you use the method in the child component it is still bound to the parent component. This is the main way of sending data back up to the parent. You could, for instance, have the method simply setState of a value on the parent component, pass the method down to the child as props with parameter requirements, pass the parameters in on the child and send it back up to parent when it is called. (Let me know if that was confusing)
There is a lot in your app that isn't very "React" so I would definitely recommend going through their documentation. Starting with: https://reactjs.org/docs/components-and-props.html and watching some videos on Youtube about state management in React.

Communication between parents is best thought of as having two methods of operating:
Parents can pass their children props
Children can call functions which are passed to them from their parents
This means that the relationship between components is defined via. their props. It's a one way method of communication that makes it much easier to build components as detached modules that happen to be connected in a given context.
So if you have something like:
Child A
/
Parent
\
Child B
Then you need to communicate via. the Parent. The children will call a function given to them by their parent, and the parent will pass down a new prop to the child components.
You can also do this thing via context, which allows you to avoid having to pass all of these props down manually, but does not affect the streams of communication available to components.
const ButtonComponent = (props) => {
return (
<div>
Child
<button onClick={props.handleClick}>
Do the thing
</button>
</div>
);
};
const CountDisplay = (props) => {
return (
<div>{props.count}</div>
);
};
const Parent = (props) => {
const parentLog = () => console.log('log from parent');
const [
currentCount,
setCurrentCount,
] = React.useState(0);
return (
<div>
Parent
<ButtonComponent handleClick={() => setCurrentCount(currentCount + 1)} />
<CountDisplay count={currentCount} />
</div>
);
};
ReactDOM.render(<Parent />, document.getElementById('app'))
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="app"></div>

Related

React state/child update not behaving as I expected it to

Inside of my react application there are two components
Navbar
import React, { Component } from 'react';
import NavLink from './navlink';
class Navbar extends Component {
state = {
links: [
{
title: "Music",
active: false
},
{
title: "Home",
active: false
},
{
title: "Discord",
active: false
}
]
}
updateNavlinks = title => {
const links = this.state.links
for (const link in links){
if (links[link].title != title){
links[link].active=false;
}
else{
links[link].active=true;
}
}
console.log(links);
this.setState({links})
};
render() {
return (
<div id="Navbar">
{this.state.links.map(link => <NavLink key={link.title} title={link.title} active={link.active} onClickFunc={this.updateNavlinks}/>) }
</div>
);
}
}
export default Navbar;
Navlink
import React, { Component } from 'react';
class NavLink extends Component {
state = {
className: "navlink"+ (this.props.active?" active":"")
}
render() {
return (
<div className={this.state.className} onClick={() => this.props.onClickFunc(this.props.title)}>
{this.props.title}
</div>
);
}
}
export default NavLink;
My intention is to create a navbar where if the user selects a page, that <Navlink /> has its state changed. Once its state is changed (active=true), I want the classname to change, adding the "active" class and giving it the styles I want.
When updateNavlinks() is called, the state in <Navbar /> is changed, but it doesn't cause a visual change in the associated <Navlink />
Where did I go wrong with this? Is there a more simple way to accomplish this?
Here, you're mutating the existing state:
updateNavlinks = title => {
const links = this.state.links
for (const link in links){
if (links[link].title != title){
links[link].active=false;
}
else{
links[link].active=true;
}
}
console.log(links);
this.setState({links})
};
Never mutate state in React - that can make the script behave unpredictably. You need to call setState with a new object instead, so React knows to re-render:
updateNavlinks = titleToMakeActive => {
this.setState({
links: this.state.links.map(
({ title, active }) => ({ title, active: title === titleToMakeActive })
)
});
};
Another problem is that you're assigning state in the constructor of the child component in NavLink:
class NavLink extends Component {
state = {
className: "navlink"+ (this.props.active?" active":"")
}
render() {
return (
<div className={this.state.className} onClick={() => this.props.onClickFunc(this.props.title)}>
{this.props.title}
</div>
);
}
}
This assigns to the state own-property when the component is mounted, but the component doesn't get un-mounted; the instance doesn't change, so state doesn't get assigned to again, even when the props change.
To fix it, reference the props inside render instead of using state:
class NavLink extends Component {
render() {
return (
<div className={"navlink"+ (this.props.active?" active":"")} onClick={() => this.props.onClickFunc(this.props.title)}>
{this.props.title}
</div>
);
}
}

Render unique divs for each hovered element

minimum reproducible example: https://codesandbox.io/s/react-hover-example-tu1eu?file=/index.js
I currently have a new element being rendered when either of 2 other elements are hovered over. But i would like to render different things based upon which element is hovered.
In the example below and in the codepen, there are 2 hoverable divs that are rendered; when they are hovered over, it changes the state and another div is rendered. I would like for the HoverMe2 div to render text "hello2". Currently, whether i hover hoverme1 or 2, they both just render the text "hello".
import React, { Component } from "react";
import { render } from "react-dom";
class HoverExample extends Component {
constructor(props) {
super(props);
this.handleMouseHover = this.handleMouseHover.bind(this);
this.state = {
isHovering: false
};
}
handleMouseHover() {
this.setState(this.toggleHoverState);
}
toggleHoverState(state) {
return {
isHovering: !state.isHovering
};
}
render() {
return (
<div>
<div
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me
</div>
<div
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me2
</div>
{this.state.isHovering && <div>hello</div>}
</div>
);
}
}
render(<HoverExample />, document.getElementById("root"));
You need to keep the state of item which you have hovered that's for sure
const { Component, useState, useEffect } = React;
class HoverExample extends Component {
constructor(props) {
super(props);
this.handleMouseHover = this.handleMouseHover.bind(this);
this.state = {
isHovering: false,
values: ['hello', 'hello2'],
value: 'hello'
};
}
handleMouseHover({target: {dataset: {id}}}) {
this.setState(state => {
return {
...state,
isHovering: !state.isHovering,
value: state.values[id]
};
});
}
render() {
return (
<div>
<div
data-id="0"
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me
</div>
<div
data-id="1"
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me2
</div>
{this.state.isHovering && <div>{this.state.value}</div>}
</div>
);
}
}
ReactDOM.render(
<HoverExample />,
document.getElementById('root')
);
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<div id="root"></div>
You can pass the context text as shown in example. This is working code:
import React, { Component } from "react";
import { render } from "react-dom";
// Drive this using some configuration. You can set based on your requirement.
export const HOVER_Hello1 = "Hello1";
export const HOVER_Hello2 = "Hello2";
class HoverExample extends Component {
constructor(props) {
super(props);
this.handleMouseHover = this.handleMouseHover.bind(this);
this.state = {
isHovering: false,
contextText: ""
};
}
handleMouseHover = (e, currentText) => {
this.setState({
isHovering: !this.state.isHovering,
contextText: currentText
});
}
toggleHoverState(state) {
//
}
render() {
return (
<div>
<div
onMouseEnter={e => this.handleMouseHover(e, HOVER_Hello1)}
onMouseLeave={e => this.handleMouseHover(e, HOVER_Hello1)}
>
Hover Me
</div>
<div
onMouseEnter={e => this.handleMouseHover(e, HOVER_Hello2)}
onMouseLeave={e => this.handleMouseHover(e, HOVER_Hello2)}
>
Hover Me2
</div>
{this.state.isHovering && <div>{this.state.contextText}</div>}
</div>
);
}
}
export default HoverExample;
If the whole point is about linking dynamically messages to JSX-element you're hovering, you may store that binding (e.g. within an object).
Upon rendering, you simply pass some anchor (e.g. id property of corresponding object) within a custom attribute (data-*), so that later on you may retrieve that, look up for the matching object, put linked message into state and render the message.
Following is a quick demo:
const { Component } = React,
{ render } = ReactDOM,
rootNode = document.getElementById('root')
const data = [
{id:0, text: 'Hover me', message: 'Thanks for hovering'},
{id:1, text: 'Hover me too', message: 'Great job'}
]
class HoverableDivs extends Component {
state = {
messageToShow: null
}
enterHandler = ({target:{dataset:{id:recordId}}}) => {
const {message} = this.props.data.find(({id}) => id == recordId)
this.setState({messageToShow: message})
}
leaveHandler = () => this.setState({messageToShow: null})
render(){
return (
<div>
{
this.props.data.map(({text,id}) => (
<div
key={id}
data-id={id}
onMouseEnter={this.enterHandler}
onMouseLeave={this.leaveHandler}
>
{text}
</div>
))
}
{
this.state.messageToShow && <div>{this.state.messageToShow}</div>
}
</div>
)
}
}
render (
<HoverableDivs {...{data}} />,
rootNode
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
As #CevaComic pointed out, you can do this with CSS. But if you want to use React, for example, because your actual problem is more complex, here is the answer.
You will need a way to tell apart the two elements. It could be done with some neat tricks, like setting an unique id to each element, passing a custom argument, or something else.
But I would advise against "cool tricks" as it's more difficult to understand what is going on, and the code is more prone to errors. I think the best way it to use a dumb approach of unique functions for unique elements.
Each onMouseEnter and onMouseLeave has to be an unique function (e.g. handleMouseHover1 and handleMouseHover2), and each of those functions need to control unique state (for example, isHovering1 and isHovering2). Then you have to render the element you want based on the state. Of course, for a real-world code, you will probably want to use more descriptive names to make the code more comprehensible. The full code would look something like this.
class HoverExample extends Component {
state = {
isHovering1: false,
isHovering2: false
};
handleMouseHover1 = () => {
this.setState(({ isHovering1 }) => ({ isHovering1: !isHovering1 }));
};
handleMouseHover2 = () => {
this.setState(({ isHovering2 }) => ({ isHovering2: !isHovering2 }));
};
render() {
const { isHovering1, isHovering2 } = this.state;
return (
<div>
<div
onMouseEnter={this.handleMouseHover1}
onMouseLeave={this.handleMouseHover1}
>
Hover Me1
</div>
<div
onMouseEnter={this.handleMouseHover2}
onMouseLeave={this.handleMouseHover2}
>
Hover Me2
</div>
{isHovering1 && <div>hello1</div>}
{isHovering2 && <div>hello2</div>}
</div>
);
}
}
Also, updated example: https://codesandbox.io/s/react-hover-example-rc3h0
Note: I have also edited the code to add some syntax sugar which exists with newer ECMAScript versions. Instead of binding the function, you can use the arrow function format, e.g. fn = () => { ... }. The arrow function means the this context is automatically bound to the function, so you don't have to do it manually. Also, you don't have to initialize this.state inside the constructor, you can define it as a class instance property. With those two things together, you do not need the constructor at all, and it makes the code a bit cleaner.

Button click won't update state in my React app

A button click shall filter my job-card array to only one category. E.g. button "Marketing" should filter to those jobs from array who have prop "jobstags: Marketing". I used a very similar procedure like for my input which filters jobs perfectly.
I can console log my event (the button click) with the according value ("Marketing"). But it still doesn't filter correctly...
In my app I did this:
export default class App extends Component {
state = {
jobs: jobs,
searchfield: '',
jobtags: ''
}
onSearchChange = event => {
this.setState({ searchfield: event.target.value })
}
onClickChange = event => {
console.log(event.target.value)
this.setState({ jobtags: event.target.value })
}
render() {
const filteredJobs = this.state.jobs.filter(job => {
return (
job.position
.toLowerCase()
.includes(this.state.searchfield.toLowerCase()) ||
job.company
.toLowerCase()
.includes(this.state.searchfield.toLowerCase()) ||
job.jobtags.toLowerCase().includes(this.state.jobtags.toLowerCase())
)
})
// this.save()
if (this.state.jobs.length === 0) {
return <Loading>Loading...</Loading>
} else {
return (
<Router>
<React.Fragment>
<Route
exact
path="/"
render={() => (
<Home
jobs={filteredJobs}
searchChange={this.onSearchChange}
clickChange={this.onClickChange}
/>
)}
/>
onClickChange is what should update the state of tags
In my Home component I then simply pass the value on to the Categories component:
<Categories clickChange={clickChange} />
Finally it arrives in my Categories component where I say:
export default class Categories extends Component {
render() {
const { clickChange } = this.props
return (
<Wrapper>
<button value="Marketing" onClick={clickChange}>
<img
alt="Button"
src={require('/Users/markus/Documents/q4-2018/jobs-app/src/img/computer.png')}
/>
Frontend
</button> ...
Any ideas? Thx!
maybe you have to bind the "this" of "onClickChange", for example in the constructor of your App class.
Example :
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
jobs: jobs,
searchfield: '',
jobtags: ''
};
this.onClickChange = this.onClickChange.bind(this);
and it will work I think
You will have to bind it. Add this line to your constructor:
this.onClickChange = this.onClickChange.bind(this);

Customized Dropdown Field Component not compatible with Redux-Form

I have the following problem.
I have customized my own DropDown component using elements.
I want this element to interact with Redux-Form as I want to save the value that is selected.
This does not work:
<Field
name="name"
component={MyCustomizedDropDown}
data={myData}/>
The other option was to use the "input" props but as I am using elements, this is not possible.
Can someone give me a solution? Thanks.
MyCustomizedDropDown component:
import React, { Component } from "react";
import PropTypes from "prop-types";
class MyCustomizedDropdown extends Component {
constructor(props) {
super(props);
this.state = {
...this.props,
items: this.props.items || [],
selectedItem: this.props.items[0] || this.props.selectedItem,
showItems: false,
isOpened: false
};
this.dropDown = this.dropDown.bind(this);
this.selectedItem = this.selectedItem.bind(this);
}
dropDown() {
this.setState(prevState => ({
showItems: !prevState.showItems
}));
}
selectedItem(item) {
this.setState({
selectedItem: item,
showItems: false
});
}
render() {
const { input } = this.props;
return (
<div className="select-box--wrapper">
<div className="select-box--toggle" onClick={this.dropDown}>
<div className="select-box--selected-item">
{this.state.selectedItem && this.state.selectedItem.value}
</div>
<MyImage
className={`${
this.state.showItems
? "select-box--arrow-rotated"
: "select-box--arrow"
}`}
/>
</div>
<div className="select-box--main">
<div
{...input} \\THIS DOES NOT WORK
className="select-box--items">
{this.state.data.map(item => (
<div key={item.id} onClick={() => this.selectedItem(item)}>
{item.value}
</div>
))}
</div>
</div>
</div>
);
}
}
MyCustomizedDropdown.propTypes = {
data: PropTypes.array,
selectedItem: PropTypes.array,
input: PropTypes.object
};
export default MyCustomizedDropdown;
redux-form only works with "controlled" components. That means the component needs a prop that a parent uses to tell it what it's value is. For example, the following is a controlled component:
<TextField
value={this.state.inputValue}
onChange={(value) => this.setState({ inputValue: value })}
/>
Note that we're telling the TextField component what it's value is. You need to change your component to work the same way. The only caveat here is that redux-form injects a prop called input that is an object containing value and onChange (and a few other things), instead of directly injecting value and onChange.
So for the example above, it needs to work like this to support redux-form:
<TextField
input={{
value: this.state.inputValue,
onChange: (value) => this.setState({ inputValue: value })
}}
/>
Here's your component written as a "controlled" component, in a way that should work with redux-form:
import React, { Component } from "react";
import PropTypes from "prop-types";
class MyCustomizedDropdown extends Component {
constructor(props) {
super(props);
this.state = {
showItems: false
};
this.dropDown = this.dropDown.bind(this);
this.selectedItem = this.selectedItem.bind(this);
}
dropDown() {
this.setState(prevState => ({
showItems: !prevState.showItems
}));
}
hideDropdownItems() {
this.setState({
showItems: false
});
}
render() {
const { input } = this.props;
return (
<div className="select-box--wrapper">
<div className="select-box--toggle" onClick={this.dropDown}>
<div className="select-box--selected-item">
{this.input.value && this.input.value.value}
</div>
<MyImage
className={`${
this.state.showItems
? "select-box--arrow-rotated"
: "select-box--arrow"
}`}
/>
</div>
<div className="select-box--main">
<div
className="select-box--items">
{this.state.data.map(item => (
<div
key={item.id}
onClick={() => {
this.input.onChange(item)
this.hideDropdownItems();
}}
>
{item.value}
</div>
))}
</div>
</div>
</div>
);
}
}
MyCustomizedDropdown.propTypes = {
data: PropTypes.array,
selectedItem: PropTypes.array,
input: PropTypes.object
};
export default MyCustomizedDropdown;
Note we tell MyCustomizedDropdown what it's value is using this.props.input.value
We call this.props.input.onChange if the component wants to change it's value. Since it can't do it on it's own, it needs to tell it's parent it wants to change value.
The parent needs to respond to onChange and update MyCustomizedDropdown's value
For example, this is how you'd use your component without redux-form:
<MyCustomizedDropdown
input={{
value: this.state.dropDownValue,
onChange: (value) => this.setState({ dropDownValue: value })
}}
/>
And with redux-form, you can simply do the following, since redux-form manages all that stuff for you:
<Field
component={MyCustomizedDropdown}
/>
You shouldn't be handling the value of the input in the input's state. MyCustomizedDropDown should receive the handleChange function, items and selectedItem as props. The only thing that should be in the component's state is it's open or not.

Making second setState in callback - bad practice?

I have a component where I am making a second setState() as a callback in the first setState(). Is this poor practice? Is there another way to call two setStates synchronously?
Initially when I called updateData() in the first setState() there was a delay in rendering the correct data in the myComponent component. It was one 'step' behind. This works, but is it conventional?
import React, { Component } from "react";
import MyComponent from "../../components/MyComponent";
import RaisedButton from "material-ui/RaisedButton";
import { generateData } from "./generateData";
class App extends Component {
constructor(props) {
super(props);
this.state = {
text: "",
data: []
};
}
updateData(){
this.setState({
data: generateData(this.state.text)
})
}
handleChange(e) {
this.setState({
text: e.target.value
}, () => {
this.updateData(this.state.text)
});
}
handleSubmit(e) {
e.preventDefault();
}
render() {
return (
<div>
<h2>Input</h2>
<form onSubmit={e => this.handleSubmit(e)}>
<textarea
value={this.state.text}
onChange={e => this.handleChange(e)}
/>
<div>
<RaisedButton type="submit"/>
</div>
</form>
<h2>Output</h2>
<MyComponent data={this.state.data} />
</div>
);
}
}
export default App;
The problem seems to be that you are updating data from this.state.text. Instead you can update both text and data in a single call by referencing the original input value:
handleChange(e) {
this.setState({
text: e.target.value,
data: generateData(e.target.value),
});
}
This is certainly preferred over making two calls to setState (which implies potentially rerender the component twice).

Categories

Resources