I have the following Code using https://ant.design/components/checkbox/, and Trying to uncheck when a checkbox has been checked.
I don't want to check all if button is click, just Uncheck all or the one checkbox selected.
constructor(props) {
super(props);
this.state = {
checked: true,
}
this.onChange = this.onChange.bind(this);
}
onChange(value) {
this.props.onChangeSession(value)
}
onChangeBox = e => {
this.setState({
checked: e.target.checked,
});
};
unChecked = () => {
this.props.onChangeSession([])
this.setState({
checked: false
});
};
render () {
const {
data,
} = this.props
return (
<div>
<Button type="primary" size="small" onClick={this.unChecked}>
Uncheck
</Button>
<Checkbox.Group
style={{ width: '100%' }}
onChange={this.onChange}>
<div>
<div className="filter-subhead">Track</div>
{data.map(i =>
<div className="filter-item">
<Checkbox
checked={this.state.checked}
onChange={this.onChangeBox}
value={i}>{i}</Checkbox>
</div>
)}
</div>
</Checkbox.Group>
</div>
)
}
Any help will be appreciate!
Working Link
The toggle on the checkbox wasn't working due to Checkbox.Group, you can simply use Checkbox
Regarding Checkbox State:
You can't have a single state for all the checkboxes, so you will need to have an array of bool which will serve as the state for each checkbox item.
In the example, I have initialized checkbox state on componentDidMount and it creates an array ([false,false,false,...]) and the exact same thing is used for resetting on Uncheck. (Possibility of refactoring in my code)
User assigned state will decide whether to check the checkbox or not.
import React from "react";
import ReactDOM from "react-dom";
import { Button, Checkbox } from "antd";
import "antd/dist/antd.css";
import "./index.css";
let data = [3423, 3231, 334234, 55345, 65446, 45237];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
checkboxArray: []
};
// this.onChange = this.onChange.bind(this);
}
componentDidMount() {
let createEmptyArray = new Array(data.length).fill(false);
this.setState({ checkboxArray: createEmptyArray });
}
onChange(e) {
console.log(e.target);
}
onChangeBox = (e, i) => {
let checkBoxCurrentState = this.state.checkboxArray;
checkBoxCurrentState[i] = !checkBoxCurrentState[i];
this.setState({
checkboxArray: checkBoxCurrentState
});
};
unChecked = e => {
let resetArray = new Array(data.length).fill(false);
this.setState({
checkboxArray: resetArray
});
};
render() {
const { data } = this.props;
return (
<div>
<Button type="primary" size="small" onClick={this.unChecked}>
Uncheck
</Button>
<div>
<div className="filter-subhead">Track</div>
{data.map((i, index) => (
<div className="filter-item">
<Checkbox
checked={this.state.checkboxArray[index] ? true : false}
onChange={e => this.onChangeBox(e, index)}
value={index}
>
{JSON.stringify(this.state.checkboxArray[index])}
</Checkbox>
</div>
))}
</div>
{JSON.stringify(this.state.checkboxArray)}
</div>
);
}
}
ReactDOM.render(<App data={data} />, document.getElementById("root"));
Simple copy and paste the above code and add props where required.
And if you want to user Checkbox.Group, will need to update the onChange method of CheckBox.Group
let data = ['Apple', 'Pancakes', 'Butter', 'Tea', 'Coffee'];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
checkboxArray: []
};
// this.onChange = this.onChange.bind(this);
}
componentDidMount() {
let createEmptyArray = new Array(this.props.data.length).fill(false);
this.setState({ checkboxArray: createEmptyArray });
}
onChangeBox = (e, i) => {
let checkBoxCurrentState = this.state.checkboxArray;
checkBoxCurrentState[i] = !checkBoxCurrentState[i];
this.setState({
checkboxArray: checkBoxCurrentState
});
};
unChecked = () => {
let resetArray = new Array(data.length).fill(false);
this.setState({
checkboxArray: resetArray
});
};
render() {
const { data } = this.props;
return (
<div>
<button onClick={this.unChecked}>Clear All</button>
{this.props.data.map((single, index) => (
<div>
<input
type="checkbox"
id={index}
name="scales"
checked={this.state.checkboxArray[index]}
onChange={e => this.onChangeBox(e, index)}
/>
<label for={index}>{single}</label>
</div>
))}
</div>
);
}
}
ReactDOM.render(<App data={data} />, document.getElementById("root"));
<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="root"></div>
If you're going to use a map to dynamically generate the checkboxes, using state to keep track of checked value will be tricky. You should not do this.
What you should do is not include the checked prop at all in the component.
<Checkbox
onChange={this.onChangeBox}
value={i}>{i}
</Checkbox>
The checkbox should still check even if you do not include the checked prop. It's a little strange, I know.
Instead just pass the value into the onChangeBox function and handle all your logic there and set a state value on change.
I just tested it out and it works.
Related
I created an update page in my react app.
To sum up; when I click a div it shows the data in input fields. For example, when I click the first field, type in there something, and click another div, the changes I made disappear. I want that if I make a change in there, It should stay there before save it. How can I do that?
<div className="detailsPage-panel-right">
{
this.state.activeFields?.fields?.map(field => {
const config = this.config.fields.find(fieldConfig =>
fieldConfig.key === field.key)
const inputConfig = {
type: config?.dataType.type,
id: config?.key,
label: config?.displayName,
required: false,
autofocus: false,
value: field.value
};
const inputBindings: ITextInputBindings = {}
return (
<div key={`${this.state.activeFields.key}-${field.key}`}>
<TextInput config={inputConfig} bindings={inputBindings}></TextInput>
</div>
)
})
}
</div>
Text input component
import "./text-field.scss";
import { Form } from "react-bootstrap";
import { Component } from "../../utils/stateless-component";
export interface ITextInputBindings {
}
export interface ITextInputConfig {
type: "text" | "dateTime" | "enumeration" | "guid" | undefined,
id: string | undefined,
label: string | undefined,
placeholder?: string,
required: boolean,
autofocus?: boolean,
value?: string
}
class TextInput extends Component<ITextInputConfig,ITextInputBindings> {
render() {
return (
<div className="textInput">
<Form.Group className="mb-3 textInput-group">
<Form.Label htmlFor={this.config.id}>{this.config.label}</Form.Label>
<Form.Control type={this.config.type}
placeholder={this.config.placeholder}
required={this.config.required}
id={this.config.id}
autoFocus={this.config.autofocus}
defaultValue={this.config.value} />
</Form.Group>
</div>
);
}
}
export default TextInput;
I think I should use onChange method but I don't know how.
key prop
Remember to check re-render when your activeFields.field changes, because you had set the key in your TextInput.
This will result in the TextInput component be unmount and create a new one
// 📌 check this state. Do not mutate to prevent re-render
this.state.activeFields?.fields?.map(field => {
const config = this.config.fields.find(fieldConfig =>
fieldConfig.key === field.key)
const inputConfig = {
type: config?.dataType.type,
id: config?.key,
label: config?.displayName,
required: false,
autofocus: false,
value: field.value
};
const inputBindings: ITextInputBindings = {}
return (
// 📌 if key be mutated from state, it will create a new component intead of old one
<div key={`${this.state.activeFields.key}-${field.key}`}>
<TextInput config={inputConfig} bindings={inputBindings}></TextInput>
</div>
)
})
Save Input value
And if you want to save the input value in TextInput, it is depends on which component you want to save the input value by state.
Save in the child component (In your case the TextInput)
Add a onChange event and a state in your TextInput component
Then add props because you are give props to it.
like this example edited from your code (maybe can not run, but the concept should work)
class TextInput extends Component<ITextInputConfig,ITextInputBindings> {
constructor(props) {
super(props);
this.state = { ...this.props }
}
// set state
const handleChange = (e) => {
this.setState({...this.state,
config: { ...this.state.config, value: e.target.value }
})
}
render() {
return (
<div className="textInput">
<Form.Group className="mb-3 textInput-group">
<Form.Label htmlFor={this.config.id}>{this.config.label}</Form.Label>
<Form.Control type={this.config.type}
placeholder={this.config.placeholder}
required={this.config.required}
id={this.config.id}
autoFocus={this.config.autofocus}
defaultValue={this.config.value}
// 📌 add onChange event on Form.Control
onChange={handleChange}
/>
</Form.Group>
</div>
);
}
}
Save in parent component
And if you need control or save state changes from parent component
add a state and a changeState function in your parent component, and give changeState to TextInput's props and let the
changeState prop mutate parent's value in child's input onChange event
example:
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = { inputValue: undefined }
}
const handleChange = (e) =>{
if(e.target)
this.setState({...this.state, inputValue: e.target.value});
}
render(){
return (
<div className="detailsPage-panel-right">
{
this.state.activeFields?.fields?.map(field => {
const config =
this.config.fields.find(fieldConfig =>
fieldConfig.key === field.key)
const inputConfig = {
type: config?.dataType.type,
id: config?.key,
label: config?.displayName,
required: false,
autofocus: false,
value: field.value
};
const inputBindings: ITextInputBindings = {}
return (
<div key=
{`${this.state.activeFields.key}-${field.key}`}
>
<TextInput
config={inputConfig}
bindings={inputBindings}
onChange={handleChange}>
</TextInput>
</div>
)
})
}
</div>
)
}
}
// TextInput
class TextInput extends Component<ITextInputConfig,ITextInputBindings> {
constructor(props) {
super(props);
this.state = { ...this.props }
}
const handleChange = (e) => {
this.props.onChange(e);
}
render() {
return (
<div className="textInput">
<Form.Group className="mb-3 textInput-group">
<Form.Label htmlFor={this.config.id}>{this.config.label} </Form.Label>
<Form.Control
type={this.config.type}
placeholder={this.config.placeholder}
required={this.config.required}
id={this.config.id}
autoFocus={this.config.autofocus}
defaultValue={this.config.value}
onChange={handleChange}/>
</Form.Group>
</div>
);
}
}
Code snippet example
a example that how child mutate parent's value, and how does the component destroyed when key changes. (written by functional component)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
function App () {
const [keys, setKeys] = React.useState([1, 2]);
const [inputValue, setInputValue] = React.useState(``);
const [inputValue2, setInputValue2] = React.useState(``);
const handleKeys = () =>{
let temp = [...keys];
temp[0] = temp[0] + 2;
temp[1] = temp[1] + 2;
setKeys([...temp])
}
return <div>
<div><button>Click this still remain the changes you had made</button></div>
<div><button onClick={handleKeys}>Click this to change keys, and will refresh the 'Number' prefix input component</button></div>
<br />
{
keys.map((key)=>{
if (key % 2 === 0) {
return <div key={key}>Number {key}: <Child setInputValue={setInputValue2}></Child></div>
}
else {
return <div key={key}>Number {key}: <Child setInputValue={setInputValue}></Child></div>
}
})
}
<br />
<div>child components that do not have key</div>
<div>First Child's Input: <Child setInputValue={setInputValue}></Child></div>
<div>Second Child's Input: <Child setInputValue={setInputValue2}></Child></div>
<br />
<div>inputValue(in parent from first child): {inputValue}</div>
<div>inputValue2(in parent from second child): {inputValue2}</div>
</div>
}
function Child ({ setInputValue }) {
const handleChange = (e) => {
if(setInputValue)
setInputValue(e.target.value);
}
return <input onChange={handleChange}></input>
}
</script>
<script type="text/babel">
ReactDOM.render(
<App></App>
, document.getElementById("root"));
</script>
Dynamically mutate and save input value by state
I guess you need save value dynamically by this.state.activeFields?.fields.
Create a state object for recording your active input value.
And add a handleChange function which can change value by e.target.id
// In your TextInput's parent
constructor(props) {
super(props);
this.state = { inputValues: {} }
}
const handleChange = (e)=>{
const changeField = this.state.activeFields?.fields.find(x=>x.key === e.target.key);
if(changeField) {
this.setState({...this.state.inputValues, changeField.key: e.target.value})
}
}
this.state.activeFields?.fields?.map( (field) => {
return (
<TextInput
config={inputConfig}
bindings={inputBindings}
// add onChange event
onChange={handleChange}
>
</TextInput>
)
})
more refernece:
Lifting State Up
Other
According to react-bootstrap's Form.Control API doc, should use the value intead of defaultValue
I have the following example where the toggleComponent.js is working perfectly.
The problem here is that I don't want to render the <ContentComponent/> inside the toggle, rather I want the opposite, I want to toggle the <ContentComponent/> that will be called in another component depending on the state of the toggle.
So the <ContentComponent/> is outside the toggleComponent.js, but they are linked together. So I can display it externally using the toggle.
An image to give you an idea:
Link to funtional code:
https://stackblitz.com/edit/react-fwn3rn?file=src/App.js
import React, { Component } from "react";
import ToggleComponent from "./toggleComponent";
import ContentComponent from "./content";
export default class App extends React.Component {
render() {
return (
<div>
<ToggleComponent
render={({ isShowBody, checkbox }) => (
<div>
{isShowBody && <h1>test</h1>}
<button onClick={checkbox}>Show</button>
</div>
)}
/>
<ToggleComponent
render={({ isShowBody, checkbox }) => (
<div>
{isShowBody && (
<h1>
<ContentComponent />
</h1>
)}
<button onClick={checkbox}>Show</button>
</div>
)}
/>
</div>
);
}
}
Bit tweaked your source.
Modified ToggleComponent
import React from "react";
export default class ToggleComponent extends React.Component {
constructor() {
super();
this.state = {
checked: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick = () => {
this.setState({ checked: !this.state.checked });
this.props.toggled(!this.state.checked);
};
checkbox = () => {
return (
<div>
<label>Toggle</label>
<span className="switch switch-sm">
<label>
<input type="checkbox" name="select" onClick={this.handleClick} />
<span />
</label>
</span>
</div>
);
};
render() {
return this.checkbox();
}
}
Added OtherComponent with ContentComponent inside.
import React, { Component } from "react";
import ContentComponent from "./content";
export default class OtherComponent extends React.Component {
render() {
return <div>{this.props.show ? <ContentComponent /> : null}</div>;
}
}
Separated as per your requirement.
Modified App
import React, { Component, PropTypes } from "react";
import ToggleComponent from "./toggleComponent";
import OtherComponent from "./otherComponent";
export default class App extends React.Component {
constructor() {
super();
this.toggled = this.toggled.bind(this);
this.state = { show: false };
}
toggled(value) {
this.setState({ show: value });
}
render() {
return (
<div>
<ToggleComponent toggled={this.toggled} />
<OtherComponent show={this.state.show} />
</div>
);
}
}
Working demo at StackBlitz.
If you want to share states across components a good way to do that is to use callbacks and states. I will use below some functional components but the same principle can be applied with class based components and their setState function.
You can see this example running here, I've tried to reproduce a bit what you showed in your question.
import React, { useState, useEffect, useCallback } from "react";
import "./style.css";
const ToggleComponent = props => {
const { label: labelText, checked, onClick } = props;
return (
<label>
<input type="checkbox" checked={checked} onClick={onClick} />
{labelText}
</label>
);
};
const ContentComponent = props => {
const { label, children, render: renderFromProps, onChange } = props;
const [checked, setChecked] = useState(false);
const defaultRender = () => null;
const render = renderFromProps || children || defaultRender;
return (
<div>
<ToggleComponent
label={label}
checked={checked}
onClick={() => {
setChecked(previousChecked => !previousChecked);
}}
/>
{render(checked)}
</div>
);
};
const Holder = () => {
return (
<div>
<ContentComponent label="First">
{checked => (
<h1>First content ({checked ? "checked" : "unchecked"})</h1>
)}
</ContentComponent>
<ContentComponent
label="Second"
render={checked => (checked ? <h1>Second content</h1> : null)}
/>
</div>
);
};
PS: A good rule of thumb concerning state management is to try to avoid bi-directional state handling. For instance here in my example I don't use an internal state in ToggleComponent because it would require to update it if given checked property has changed. If you want to have this kind of shared state changes then you need to use useEffect on functional component.
const ContentComponent = props => {
const { checked: checkedFromProps, label, children, render: renderFromProps, onChange } = props;
const [checked, setChecked] = useState(checkedFromProps || false);
const defaultRender = () => null;
const render = renderFromProps || children || defaultRender;
// onChange callback
useEffect(() => {
if (onChange) {
onChange(checked);
}
}, [ checked, onChange ]);
// update from props
useEffect(() => {
setChecked(checkedFromProps);
}, [ checkedFromProps, setChecked ]);
return (
<div>
<ToggleComponent
label={label}
checked={checked}
onClick={() => {
setChecked(previousChecked => !previousChecked);
}}
/>
{render(checked)}
</div>
);
};
const Other = () => {
const [ checked, setChecked ] = useState(true);
return (
<div>
{ checked ? "Checked" : "Unchecked" }
<ContentComponent checked={checked} onChange={setChecked} />
</div>
);
};
I am trying to run a function on a Todo list that when a todo is added. A function will check the input box to check if its empty and if its empty, not do anything.
However, after using the function to check if the input is empty, it always returns False even when its empty. Where is the bug here?
The function name in question is "checkInput()" and it runs from the main submit button on the page
import React from "react";
import "./App.css";
import { isTemplateElement } from "#babel/types";
class TodoListt extends React.Component {
state = {};
constructor(props) {
super(props);
this.state = {
userInput: "",
list: [],
};
}
changeUserInput(input) {
this.setState({
userInput: input
})
}
addToList() {
const { list, userInput } = this.state;
this.setState({
list: [...list, {
text: userInput, key: Date.now(), done: false
}],
userInput: ''
})
}
handleChecked(e, index) {
console.log(e.target.checked);
const list = [...this.state.list];
list[index] = { ...list[index] };
list[index].done = e.target.checked;
this.setState({
list
})
}
checkInput() {
console.log(this.state.userInput);
userInput: '' ? console.log("True") : console.log("False")
}
render() {
return (
<div className="to-do-list-main">
<input
onChange={(e) => this.changeUserInput(e.target.value)}
value={this.state.userInput}
type="text"
/>
<button onClick={() => { this.checkInput(); { this.addToList(this.state.userInput) } }}>Add todo</button>
{this.state.list.map((list, index) => (
<div className="form">
<ul>
<li><input type="checkbox" onChange={(e) => this.handleChecked(e, index)} />
<span style={{ textDecoration: list.done ? 'line-through' : 'inherit' }}>
{list.text}
</span>
</li>
</ul>
</div>
))}
</div>
);
}
}
export default TodoListt;
I have this wrapper class that is used because I am using Formik and the FieldArray
import React, { Component } from "react";
import { ReactDOM } from "react-dom";
import Select from "react-select";
import { observer } from "mobx-react";
import { axiosInstance } from "../stores/AxiosInstance";
#observer
export default class CountryStateSelectComponent extends React.Component {
constructor(props) {
super(props);
this.state = { stateOptions: [] };
}
handleCountryChange = value => {
const that = this;
axiosInstance
.get(`/States?countryId=${value.value}`)
.then(function(response) {
that.props.onChange(that.props.countryName, value);
that.props.onChange(that.props.stateName, null);
const states = response.data.map(state => {
return { label: state.name, value: state.id };
});
// if I move out state select code then won't need to update state here but don't know how to call something like updateState(record)
that.setState({
stateOptions: states
});
});
};
handleStateChange = value => {
console.log(this.props.stateName, value)
this.props.onChange(this.props.stateName, value);
};
handleCountryBlur = () => {
this.props.onBlur(this.props.countryName, true);
};
handleStateBlur = () => {
this.props.onChange(this.props.stateName, true);
};
render() {
const props = this.props;
return (
<React.Fragment>
<div className="field">
<label className="label">Country</label>
<div className="control">
<Select
options={props.options}
isMulti={props.isMulti}
onChange={this.handleCountryChange}
onBlur={this.handleCountryBlur}
closeMenuOnSelect={props.closeMenuOnSelect}
/>
{this.props.CountryError}
</div>
</div>
<div className="field">
<label className="label">State/Province</label>
<div className="control">
<Select
options={this.state.stateOptions}
onChange={this.handleStateChange}
onBlur={this.handleStateBlur}
/>
{this.props.StateError}
</div>
</div>
</React.Fragment>
);
}
}
However what I found is that when the State gets selected the value does not get stored in Formik(it gets stored as undefined and sometimes true).
So now I am thinking maybe moving out the State Zip out and making it's own wrapper or something but I don't know how to get the "states" that came back and populate the correct state box as they can generate many.
#inject("AccountSetupStore")
#observer
export default class MyComponent extends Component {
constructor(props) {
super(props);
this.state = { records: [this.generateRecord(1, true, true)] };
}
componentDidMount() {
const accountSetupStore = this.props.AccountSetupStore;
accountSetupStore.getCountries();
}
updateState(record) {
// would like to call this method that can somehow update the record
// propblem is I don't seem to have access to props when this function is being called from the CountryStateSelectComponent
}
render() {
const props = this.props;
const accountSetupStore = props.AccountSetupStore;
const countries = [];
for (const country of accountSetupStore.countries) {
countries.push({ value: country.id, label: country.name });
}
return (
<section className="accordions">
<Formik
initialValues={{
records: this.state.records
}}
onSubmit={(
values,
{ setSubmitting, setErrors}
) => {
console.log(values,"values");
}}
validationSchema={Yup.object().shape({
branches: Yup.array()
.of(
Yup.object().shape({
})
)
})}
render={({
values,
setFieldValue,
setFieldTouched,
}) => (
<FieldArray
name="records"
render={arrayHelpers => (
<Form>
{values.records.map((record, index) => {
return (<article}>
<CountryStateSelectComponent options={countries}
onChange={setFieldValue}
countryName={`records[${index}].selectedCountry`}
stateName={`records[0].selectedState`}
onBlur={setFieldTouched}
isMulti={false}
index = {index}
closeMenuOnSelect={true}
CountryError = {<ErrorMessage name={`records[${index}].selectedCountry`}/>}
StateError= {<ErrorMessage name={`records[${index}].selectedState`}/>}
/>
</article>)
})}
</Form>
)}
/>
)}
/>
</section>
);
}
}
React Select onChange sends the value to the method supplied
const onStateChange = (selectedOption, {action}) => {
//do something with the selectedOption according to the action
}
<Select onChange={onStateChange} />
See the documentation for the onChange in the Props documentation.
Im trying to update a component's state when a different component's state changes.
Here is the main component that contains the form:
class IpsumForm extends React.Component {
state = {
character: 'All',
paragraphs: 1,
ipsumDisplayed: false
};
handleInputChange = (e) => {
const target = e.target;
this.setState({ paragraphs: target.value });
};
handleSelectChange = (e) => {
const target = e.target;
this.setState({ character: target.value });
};
handleReset = (e) => {
this.setState({
character: 'All',
paragraphs: 1,
ipsumDisplayed: false
});
};
handleSubmit = (e) => {
e.preventDefault();
this.setState({
ipsumDisplayed: true
});
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<p>Select Your Character:</p>
<select
value={this.state.character}
onChange={this.handleSelectChange}
name="characterPick"
>
<option value="All">All</option>
<option value="Michael">Michael</option>
<option value="Dwight">Dwight</option>
<option value="Jim">Jim</option>
<option value="Andy">Andy</option>
<option value="Creed">Creed</option>
</select>
<div className="length">
<p>How Many Paragraphs?</p>
<input
onChange={this.handleInputChange}
name="paragraphLength"
type="text"
/>
</div>
<hr />
<input
id="submit"
type="submit"
value="Bibity Boppity Give Me The Zoppity"
/>
<button onClick={this.handleReset}>Reset</button>
</form>
<br />
<IpsumText
person={this.state.character}
length={this.state.paragraphs}
displayed={this.state.ipsumDisplayed}
/>
</div>
);
}
}
export default IpsumForm;
And here is the component that I would like to return the ipsum within a textbox:
class IpsumText extends React.Component {
state = {
value: ''
};
handleValueChange = (e) => {
console.log(data.quote1);
this.setState({
value: data.quote1
});
};
render() {
let character = this.props.person;
let paragraphs = this.props.length;
let displayed = this.props.displayed;
return (
<div className="returned-ipsum">
<textarea value={this.state.value} onChange={this.handleValueChange} />
</div>
);
}
}
export default IpsumText;
The code I have now doesn't work because a state change from the IpsumForm doesn't cause the textarea onChange handler to run in the IpsumText
I'm wondering if there is a way to do this other than using a lifecycle method because it seems like i'm just overthinking it
Here's a very simple way of looking at it:
class App extends React.Component {
state = {
firstState: 1,
}
handleStateUpdate = () => {
this.setState(prev => ({
firstState: prev.firstState + 1
}))
}
render() {
console.log(this.state.firstState)
return (
<Foo handleStateUpdate={this.handleStateUpdate} />
)
}
}
class Foo extends React.Component {
state = {
otherState: 1,
}
handleLocalStateUpdate = () => {
this.props.handleStateUpdate()
this.setState(prev => ({
otherState: prev.otherState + 1
}))
}
render() {
return <button onClick={() => this.handleLocalStateUpdate()}>Click here!</button>
}
}
ReactDOM.render(<App />, document.getElementById('app'))
<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>
<div id='app'></div>
What this is doing is passing down a function from the parent that updates the parent state, down to the child component. Once you define a function that updates the state of the child component, you can call that passed down function to also update the parent state.
Hopefully, the principle of doing this helps with the issue you're facing.
Here's a link to the code in case you wanna mess with it:
CodePen