Having a component re-render on button press in React - javascript

I have a simple React component that I'm working on creating right now. Basically, the user can input an ID and when they submit, it will display some information that is in a container. The code looks like so
export default class IDContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
Id: '',
isSubmitted: false
};
}
handleSubmit = (event) => {
this.setState({
isSubmitted: true
});
};
handleChange = (event) => {
this.setState({
Id: event.target.value
});
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}}
>
<Input type={'text'} placeholder={"Enter Id"} value={this.state.Id} onChange={this.handleChange} />
<Button type={'submit'} > Lookup </Button>
</div>
<div>
{this.state.isSubmitted && <DetailsContainer Id={this.state.Id} />}
</div>
</form>
);
}
}
The details container has already been created and just returns some details about the Id that has been passed in. I can show the details of the first Id that I pass in just fine. However, when I enter in another Id and submit the form, the DetailsContainer is not re-rendering and is still showing the details for the older Id. I tried moving it around and adding some logic (I even put the DetailsContainer in my state to see if I can manipulate it that way), but that doesn't seem to be working. I see that there is a shouldComponentUpdate() method, and that seems to be what I need to use, but the guides I saw all place it inside of the DetailsContainer. Anyway for me to have it in IDContainer, or is there an easier way to re-render the DetailsContainer?

I think part of the issue here is that once isSubmitted is set, every change you make to the input will be applied to this.state.Id and passed into DetailsContainer.
I think you'd be better off having one variable for tracking the input state, and variable one for tracking the Id you want to pass into DetailsContainer.
state = { Id: null, inputId: '' };
handleSubmit = (event) => {
this.setState({
Id: this.state.inputId
});
};
handleChange = (event) => {
this.setState({
inputId: event.target.value
});
};
render() {
return (
...
<Input ... value={this.state.inputId} />
...
{this.state.Id !== null ? <DetailsContainer Id={this.state.Id} /> : null}
);
}

Related

Problem with the confirmation button. The Button does not work

I have created a button that saves the entered data, however when I click on it, nothing happens.Here is the code.
class DefinesPagePresenter extends Component {
constructor(props) {
super(props);
this.state = {
isNeedOpenInfoWindow: false,
filesContentDict: {
[props.iniType]: props.json
}
};
}
onChange = () => {
if (this.state.filesContentDict[this.props.iniType]) {
this.props.changeInitFileParams(this.props.market, this.props.iniType, this.state.filesContentDict[this.props.iniType]);
this.setState({ isNeedOpenInfoWindow: true });
}
}
<form onSubmit={(e) => {
e.preventDefault()
this.onChange()
}}>
<div height="200%" style={{
margin: '20px 0px 0px 40px'
}}><input type="submit" value="Ok" className="c4t-button" height="200%" size="50px" /></div>
</form>
The following similar snippet to your code shows that the alert does run when clicking on the <input type='submit' /> without seeing your code there could be other problems or this.state is not what you think it is within that function (improper scoping or just at the time it is false so it doesn't run what is within the if statement).
I suggest you have an else { for the event Handler which you called onChange: so you can see if that's triggered for example it seems like you are waiting for a prop named json= to be filled in and maybe it is not when you try clicking. You might consider disabling the button until this.props.json is there.
onChange = () => {
if (this.state.filesContentDict[this.props.iniType]) {
//also make sure this method is actually running
console.log('about to run: changeInitFileParams')
this.props.changeInitFileParams(this.props.market, this.props.iniType, this.state.filesContentDict[this.props.iniType]);
this.setState({ isNeedOpenInfoWindow: true });
}
else {
alert('JSON Was not yet loaded')
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isNeedOpenInfoWindow: false,
filesContentDict: {
[props.iniType]: props.json
}
};
}
onConfirm = () => {
if (this.state.filesContentDict[this.props.iniType]) {
this.props.alertInputs(JSON.stringify({
statement: this.state.filesContentDict[this.props.iniType].statement,
iniType: this.props.iniType
}, null, 4))
this.setState({
isNeedOpenInfoWindow: true
}, console.log) // should reflect the state immediately after change
}
else {
alert('false')
}
}
render() {
return (
<form
style={{ background: 'green', height: 300 }}
onSubmit={(e) => {
e.preventDefault();
this.onConfirm();
}}
>
<input
type='submit'
value='Ok'
/>
{this.state.isNeedOpenInfoWindow &&
<div style={{
display: 'flex', justifyContent: 'space-between', alignItems: 'center', margin: '6rem' }}>
<div>
iniType:<br />
statement: <br />
</div>
<div>
{this.props.iniType} <br />
{this.state.filesContentDict[this.props.iniType].statement}
</div>
</div>
}
</form>
);
}
}
ReactDOM.render(
<App
iniType='load'
alertInputs={inputs => alert(inputs)}
json={{ statement: 'The quick Brown Fox jumped over the lazy dog!' }}
/>,
window.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>
I find it a bit strange (but I'm not sure what the exact situation is with your props) that it seems like you send the changeInitFileParams to an outer/parent React Component and then change the state of this child to isNeedOpenInfoWindow it would make more sense to change that state to the parent in most situations if the parent needs the changeInitFileParams to run.
In short nothing is not going to work with any of the code you're actually shown (I proved that it works all the functions get called) and the alert shows up. Whatever is not working is not displayed here: I'd be most suspicious about json={neverBeingDefined} or isNeedOpenInfoWindow being on this Component's state vs on the parent. (Assuming the render(){ method returns that form and some sort window that needs the state: isNeedOpenInfoWindow.
I think you should change your from onSubmit like this
onsubmit={(event)=> onChange(event)}
then use this code on onChange =>
const onChange = (event) => {
e.preventDefault();
if (this.state.filesContentDict[this.props.iniType]) {
this.props.changeInitFileParams(this.props.market, this.props.iniType,
this.state.filesContentDict[this.props.iniType]);
this.setState({ isNeedOpenInfoWindow: true });
}
}
The main reason you getting error because you are not using button. You are using input tag.
Change
<button type="submit" className="c4t-button" height="200%" size="50px">Ok</button>

How to pass TextField with entered data into form after clicking a button?

I have a TextField and a Button (both are material-ui components) displaying on my main page. I want to be able to click the button and populate a form that includes the previous TextField and any text that had already been written in it. The code I currently have just makes a new instance of the TextField within the form, while keeping the original TextField as well. How can I bring the existing TextField over into the form without duplicating?
FormTextField.js
const FormTextField = props => {
return (
<TextField
fullWidth={true}
multiline={true}
rows="10"
variant="outlined"
>
{props.data}
</TextField>
)
}
export default class FormTextField extends Component {
render() {
data={this.props.data} />
return (
{FormTextField}
);
}
};
Form.js
const Form= () => {
return (
<FormLabel">Input Text...</FormLabel>
<FormTextField />
);}
export default Form;
App.js
const AddButton= (props) => {
return (
<Button variant="contained" onClick={props.onClick}>
New Interaction
</Button>
)
}
export default class App extends Component {
constructor(props) {
super(props);
this.state = {show: false};
}
showForm = () => {
this.setState({
show: true,
});
}
render() {
return(
<Fragment>
<Header />
<FormTextField />
<AddButton onClick={this.showInteractionForm} />{this.state.show ?
<Form /> : null}
</Fragment>
);
}
};
As you want to share data between two components you can resolve this in different ways, based in your code, a solution could be:
Your App control the data so,
in your state can add:
this.state = {
inputData = '';
}
You need to pass an update function to your FromTextField
<FormTextField onTextUpdate={(text) => this.setState({ inputData: text })} />
Your form field must be controlled by App so you need to pass the data to be shown too:
<FormTextField data={this.state.inputData} onTextUpdate={(text) => this.setState({ inputData: text })} />
(you need to add that modification to FormTextField, they are easy)
And the last step is to do the same with Form
<Form data={this.state.inputData} onTextUpdate={(text) => this.setState({ inputData: text })} />
Inside Form you need to pass data and onTextUpdate to the FormTextField
You can refactor (text) => this.setState({ inputData: text }) to be a method in class.
EDIT
https://codesandbox.io/embed/stackoverflow-form-react-9188m you can find the implementation about I told you before.

React - handleChange method not firing causing selected option name not to update

I have a <Select> component from react-select renders a couple options to a dropdown, these options are fetched from an api call, mapped over, and the names are displayed. When I select an option from the dropdown the selected name does not appear in the box. It seems that my handleChange method is not firing and this is where I update the value of the schema name:
handleChange = value => {
// this is going to call setFieldValue and manually update values.dataSchemas
this.props.onChange("schemas", value);
This is not updating the value seen in the dropdown after something is selected.
I'm not sure if I'm passing the right thing to the value prop inside the component itself
class MySelect extends React.Component {
constructor(props) {
super(props);
this.state = {
schemas: [],
fields: [],
selectorField: ""
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
axios.get("/dataschemas").then(response => {
this.setState({
schemas: response.data.data
});
console.log(this.state.schemas);
});
}
handleChange = value => {
// this is going to call setFieldValue and manually update values.dataSchemas
this.props.onChange("schemas", value);
const schema = this.state.schemas.find(
schema => schema.name === value.target.value
);
if (schema) {
axios.get("/dataschemas/2147483602").then(response => {
this.setState({
fields: response.data.fields
});
console.log(this.state.fields);
});
}
};
updateSelectorField = e => {
this.setState({ selectorField: e.target.value });
};
handleBlur = () => {
// this is going to call setFieldTouched and manually update touched.dataSchemas
this.props.onBlur("schemas", true);
};
render() {
return (
<div style={{ margin: "1rem 0" }}>
<label htmlFor="color">
DataSchemas -- triggers the handle change api call - (select 1){" "}
</label>
<Select
id="color"
options={this.state.schemas}
isMulti={false}
value={this.state.schemas.find(
({ name }) => name === this.state.name
)}
getOptionLabel={({ name }) => name}
onChange={this.handleChange}
onBlur={this.handleBlur}
/>
{!!this.props.error && this.props.touched && (
<div style={{ color: "red", marginTop: ".5rem" }}>
{this.props.error}
</div>
)}
</div>
);
}
}
I have linked an example showing this issue.
In your handleChange function you are trying to access value.target.value. If you console.log(value) at the top of the function, you will get:
{
id: "2147483603"
selfUri: "/dataschemas/2147483603"
name: "Book Data"
}
This is the value that handChange is invoked with. Use value.name instead of value.target.value.

Updating 'state' dynamically in React overwrites entire state

Ok, so I'm trying to build a form in React where I can enter values in multiple inputs, then submit and have the values populate designated cells in a table. I'm trying to get the state to update using 'onChange', but when I enter the values, my initial state gets overwritten entirely.
So, if I set:
state = {
Jan012019: {
first: null,
second: null
}
};
then try to update state by entering '3' into the input for 'first' using:
this.setState(
{
Jan012019: {
[e.target.name]: e.target.value
}
},
function() {
console.log(this.state);
}
);
state displays as:
Jan012019 {
first: '3'
}
completely removing 'second' from state, and if I try to then also enter values into the 'second' input, it removes 'first' from the state. What's going on here? I've seen other examples and solutions, and I'm fairly certain my code was exactly like a solution from another question on here, but still won't work correctly. Full code below.
import React from "react";
class InputForm extends React.Component {
state = {
Jan012019: {
first: null,
second: null
}
};
updateTable = e => {
this.setState(
{
Jan012019: {
[e.target.name]: e.target.value
}
},
function() {
console.log(this.state);
}
);
};
onClick = e => {
e.preventDefault();
console.log(this.state);
};
render() {
return (
<form className="ui form" style={{ marginTop: "50px" }}>
<div className="inline field">
<label style={{ marginRight: "27px" }}>First Input</label>
<input
name="first"
type="number"
placeholder="Enter value"
onChange={this.updateTable}
/>
</div>
<div className="inline field">
<label>Second Input</label>
<input
name="second"
type="number"
placeholder="Enter value"
onChange={this.updateTable}
/>
</div>
<button onClick={this.onClick}>Click</button>
</form>
);
}
}
export default InputForm;
I also tried setting the input values to:
value={this.state.Jan012019.first}
to see if that made any difference, but no go.
That is because you are resetting the whole Jan012019 object in your setState.
Jan012019: {
[e.target.name]: e.target.value
}
You need to spread the original Jan012019 object first, to preserve the other fields
this.setState({
Jan012019: {
...this.state.Jan012019, [e.target.name]: e.target.value
}
})

How to disable a button when state changes in React [duplicate]

This question already has answers here:
How to disable button in React.js
(8 answers)
Closed 3 years ago.
I am using trying to disable a button in react based on couple states. Down below is a breakdown of my code
constructor(props) {
super(props);
this.state = {
email: '',
pass: '',
disabled: true
}
this.handleChange = this.handleChange.bind(this);
this.handlePass = this.handlePass.bind(this);
}
pretty self explanatory constructor. The disabled will be changed as state changes. My render method looks something like this
render() {
if(this.state.email && this.state.pass) {
this.setState({ disabled: false })
}
return (
<div className='container'>
<div className='top'></div>
<div className='card'>
<MuiThemeProvider>
<Card >
<div className='wrapper'>
<TextField
hintText="Email"
value={this.state.email} onChange={this.handleChange}
/><br/>
<TextField
hintText="Password"
type="password"
/><br/>
<div className='login-btn'>
<RaisedButton label="Login" primary={true}
disabled={this.state.disabled} />
</div>
</div>
</Card>
</MuiThemeProvider>
</div>
</div>
)
}
As you can see I have 2 text fields and I am handeling the data changes with the following method
handleChange(e) {
this.setState({email: e.target.value});
}
handlePass(e) {
this.setState({pass: e.target.value});
}
Now my button is initially disabled and everytime a state is changed and component re-renders I want to check for state changes and enable button accordingly. So I was thinking of using the life cycle method like so
componentWillMount() {
if(this.state.pass && this.state.disabled) {
this.setState({disabled: false})
}
}
However, this doesn't work. When both email and password field is not empty the button stays disabled. I am not sure what am I doing wrong.
Please, do not set states inside render() function. That might cause infinite loops to occur.
Refer: https://github.com/facebook/react/issues/5591
Instead of setting states inside render() function, you can set the disabled state inside the handleChange() and handlePass() function.
If more detail required, please do mention.
You should be setting the disabled state inside your handleChange and handlePass functions.
componentWillMount() only runs right before the component is rendered, but never again.
Just made a demo , is that you need, check the code in the demo below
demo
Change below code :
class App extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
pass: '',
invalidData: true
}
this.onEmailChange = this.onEmailChange.bind(this);
this.onPasswordChange = this.onPasswordChange.bind(this);
}
// componentWillUpdate is to be deprecated
//componentWillUpdate(nextProps, nextState) {
// nextState.invalidData = !(nextState.email && nextState.pass);
//}
onEmailChange(event) {
this.setState({ email: event.target.value });
}
onPasswordChange(event) {
this.setState({ pass: event.target.value });
}
render() {
return (
<form>
<input value={this.state.email} onChange={this.onEmailChange} placeholder="Email" />
<input value={this.state.password} onChange={this.onPasswordChange} placeholder="Password" />
// from this <button disabled={this.state.invalidData}>Submit</button>
//to
<button disabled={!(this.state.email && this.state.password)}>Submit</button>
</form>
);
}
}
**updated **
disable submit button in <button disabled={!(this.state.email && this.state.password)}>Submit</button> itself.

Categories