React, a modal data only renders properly on second opening - javascript

I have a react modal, which has a group of checkboxes. Those checkboxes "checked" value comes from an API call. But the problem is, that they only get updated on the second click.
I have tried to set a condition to wait for the fetch of the data before loading the group of checkboxes. So mi app looks like this
In my parent component where I load my data
I have set a permUpdated state, which equals to false, but once the data is loaded, i set it to true
getPermissionsValue() {
API.get('/user/' + this.state.rowId + '/permission')
.then(response => {
this.setState({
permissionValue: response.data,
permUpdated: true
}, () => {
// console.log(this.state.permissionValue)
});
})
}
That state is passed as a prop to the child component which is the modal
<EditUserModal permUpdated={this.state.permUpdated} ....>
And in the childs render, I used to have this, and it worked properly but with doesnt get updated
<div className="checkboxesIn">
{permissionsPrint}
</div>
{permissionsPrint} are the checkboxes that I want to render. So ive set it like:
<div className="checkboxesIn">
{this.props.permUpdated ? {permissionsPrint} : null}
</div>
But that way my app crashes.
×
Error: Objects are not valid as a React child (found: object with keys {permissionsPrint}). If you meant to render a collection of children, use an array instead.
This is how permissionPrint looks
(5) [{…}, {…}, {…}, {…}, {…}]
0: {$$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: {…}, …}
1: {$$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: {…}, …}
2: {$$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: {…}, …}
3: {$$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: {…}, …}
4: {$$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: {…}, …}
length: 5
__proto__: Array(0)
This is how i create it
var permissionsPrint = [];
var valuesData = []
this.props.permissionValue.map(e => valuesData = Object.entries(e))
console.log(valuesData)
for (let i = 1; i < valuesData.length; i++) {
//console.log(valuesData[i][0]) //name of the permission
//console.log(valuesData[i][1]) //true false
permissionsPrint.push(<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id={valuesData[i][0]} value={valuesData[i][1]} defaultChecked={valuesData[i][1]} onChange={this.props.handleChangePermissions} />
<label class="form-check-label" for={"inlineCheckbox" + i} style={{ textTransform: "capitalize" }}>{valuesData[i][0]}</label>
</div>)
EDIT: Whole code of the modal
class EditUserModal extends React.Component {
constructor(props) {
super(props);
this.state = {
userId: ""
};
console.log("PROOPPSS");
console.log(props);
console.log(this.props.permUpdated);
}
componentDidUpdate() {}
componentDidMount() {
this.setState({
email: this.props.email
});
}
render() {
var permissionsPrint = [];
var valuesData = [];
this.props.permissionValue.map(e => (valuesData = Object.entries(e)));
console.log(valuesData);
for (let i = 1; i < valuesData.length; i++) {
//console.log(valuesData[i][0]) //name of the permission
//console.log(valuesData[i][1]) //true false
permissionsPrint.push(
<div class="form-check form-check-inline">
<input
class="form-check-input"
type="checkbox"
id={valuesData[i][0]}
value={valuesData[i][1]}
defaultChecked={valuesData[i][1]}
onChange={this.props.handleChangePermissions}
/>
<label
class="form-check-label"
for={"inlineCheckbox" + i}
style={{ textTransform: "capitalize" }}
>
{valuesData[i][0]}
</label>
</div>
);
}
console.log("this is THE PROP OF PERMUPDATED");
console.log(this.props.permUpdated);
if (!this.props.show) {
return null;
}
return (
<div className="modalBg">
<div className="flex-container">
<div id="open-modal" className="modal-window">
<form onSubmit={this.props.handleSubmit}>
<div>
{/* <p>{this.props.userId}</p> */}
<FontAwesomeIcon
className="closeIcon"
onClick={this.props.close}
icon={faTimesCircle}
/>
<br></br>
{this.props.userDataUpdated ? (
<Alert
className="alertEditUser"
variant="success"
dismissible
onClose={this.props.handleDismiss}
>
User Data Updated
</Alert>
) : null}
{this.props.passwordMatchFailure ? (
<Alert
className="alertEditUser"
variant="danger"
dismissible
onClose={this.props.handleDismiss}
>
Passwords do not match
</Alert>
) : null}
{this.props.emailCantBeBlank ? (
<Alert
className="alertEditUser"
variant="danger"
dismissible
onClose={this.props.handleDismiss}
>
Email Cant Be Blank
</Alert>
) : null}
<div class="form-group emailgroup">
<label for="exampleFormControlInput1">
Change Email Address
</label>
<input
type="email"
class="form-control"
placeholder="name#example.com"
value={this.props.email}
onChange={this.props.handleChangeMail}
/>
</div>
{/* <div class="form-group emailgroup">
<label for="exampleFormControlInput1">Old Password</label>
<input type="password" class="form-control" />
</div> */}
<div class="form-group emailgroup">
<label for="exampleFormControlInput1">New Password</label>
<input
type="password"
class="form-control"
placeholder="Input new password"
onChange={this.props.handleChangePass}
/>
<input
type="password"
class="form-control"
placeholder="Confirm new password"
onChange={this.props.handleChangePass2}
style={{ marginTop: "5px" }}
/>
</div>
<div class="form-group emailgroup">
<label for="exampleFormControlInput1">User Permissions</label>
<br></br>
<div className="checkboxes">
<div className="checkboxesIn">
{/* {permissionsPrint} */}
{this.props.permUpdated ? permissionsPrint : null}
</div>
</div>
</div>
<div class="text-center">
<button class="btn btn-primary " type="submit">
Update
</button>
</div>
</div>
</form>
</div>
</div>
</div>
);
}
}
export default EditUserModal;
Whole code of the admin
import React, { Component } from "react";
import API from "../services/axiosObject.js";
import "./css/Admin.css";
import Reactable from "reactable";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faEdit } from "#fortawesome/free-regular-svg-icons";
import EditUserModal from "./EditUserModal";
export default class Admin extends Component {
constructor(props) {
super(props);
this.state = {
retracted: false,
userList: [],
showModal: false,
rowId: "",
selectedMail: "",
password: "",
password2: "",
userDataUpdated: false,
passwordMatchFailure: false,
emailCantBeBlank: false,
permissionValue: [],
permUpdated: false
};
this.getUserList = this.getUserList.bind(this);
this.showModal = this.showModal.bind(this);
this.closeModal = this.closeModal.bind(this);
this.handleChangeMail = this.handleChangeMail.bind(this);
this.handleChangePass = this.handleChangePass.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleDismiss = this.handleDismiss.bind(this);
this.handleChangePermissions = this.handleChangePermissions.bind(this);
this.getPermissionsValue = this.getPermissionsValue.bind(this);
}
componentDidUpdate() {}
componentDidMount() {
this.getUserList();
}
handleChangeMail = evt => {
this.setState({
selectedMail: evt.target.value
});
};
handleChangePass = evt => {
this.setState({
password: evt.target.value
});
};
handleChangePass2 = evt => {
this.setState({
password2: evt.target.value
});
};
handleChangePermissions = evt => {
console.log(this.state.permissionValue);
console.log("id: " + evt.target.id);
var idu = evt.target.id;
var checked = evt.target.checked;
console.log("checked: " + evt.target.checked);
var data = this.state.permissionValue[0];
console.log("data1");
console.log(data);
data[idu] = checked;
console.log("data2");
console.log(data);
this.setState({
permissionValue: [data]
});
};
getUserList() {
API.get("/userlist").then(response => {
this.setState(
{
userList: response.data
},
() => {
console.log(this.state.userList);
}
);
});
}
handleSubmit(event) {
event.preventDefault();
//console.log("updating")
//console.log("email: " + this.state.selectedMail)
var email = this.state.selectedMail;
var password = this.state.password;
var password2 = this.state.password2;
var permissionValue = this.state.permissionValue;
console.log("....");
console.log("password: " + this.state.password);
console.log("password2: " + this.state.password2);
console.log("....");
console.log("userId: " + this.state.rowId);
console.log("email: " + email);
if (password2 != password || email == "") {
console.log("P2: " + password2);
console.log("P1: " + password);
if (password2 != password) {
console.log("CONTRASEÑAS DISTINTAS");
this.setState({
passwordMatchFailure: true
});
} else {
this.setState({
emailCantBeBlank: true
});
}
} else {
console.log("ENTRA EN EL ELSE");
if (password == undefined || password2 == undefined) {
password = "";
password2 = "";
}
API.post("/user/update/" + this.state.rowId, {
email,
password,
permissionValue
}).then(response => {
console.log(permissionValue);
if (response.data == "user data updated") {
this.setState(
{
userDataUpdated: true
},
() => {
console.log(this.state.userDataUpdated);
}
);
}
});
}
}
handleDismiss() {
console.log("HANDLING DISMISSSSSSSSSSSSSSSSS");
this.setState({
userDataUpdated: false,
passwordMatchFailure: false,
emailCantBeBlank: false
});
}
showModal(rowId, rowEmail) {
this.setState(
{
showModal: true,
rowId: rowId,
selectedMail: rowEmail
},
() => {
this.getPermissionsValue();
}
);
}
closeModal() {
console.log("CLOOOOOOSSSSINNGGGGGGGGGGGGG");
this.setState(
{
showModal: false
},
() => {
// console.log("clicked closeModal")
// console.log(this.state.showModal)
}
);
}
getPermissionsValue() {
API.get("/user/" + this.state.rowId + "/permission").then(response => {
this.setState(
{
permissionValue: response.data,
permUpdated: true
},
() => {}
);
});
}
render() {
var users = this.state.userList;
const Table = Reactable.Table,
Td = Reactable.Td,
Tr = Reactable.Tr;
if (users.length === 0) {
return <p>loading</p>;
}
return (
<div class="maincontainer">
<div className="content-landing">
<button
class="btn btn-primary "
style={{ float: "right", marginRight: "20px" }}
onClick={() => this.props.history.push("/register")}
>
New user
</button>
<Table
className="table"
filterable={["Email"]}
itemsPerPage={8}
currentPage={0}
sortable={true}
>
{users.map(row => {
return (
<Tr className={row.className}>
<Td column="Email">{row.email}</Td>
<Td column="Edit">
<FontAwesomeIcon
className="editIcon"
onClick={() => this.showModal(row.id, row.email)}
icon={faEdit}
/>
</Td>
</Tr>
);
})}
</Table>
<EditUserModal
permUpdated={this.state.permUpdated}
permissionValue={this.state.permissionValue}
emailCantBeBlank={this.state.emailCantBeBlank}
userDataUpdated={this.state.userDataUpdated}
handleChangePermissions={this.handleChangePermissions}
passwordMatchFailure={this.state.passwordMatchFailure}
handleDismiss={this.handleDismiss}
show={this.state.showModal}
close={this.closeModal}
userId={this.state.rowId}
email={this.state.selectedMail}
handleChangePass={this.handleChangePass}
handleChangePass2={this.handleChangePass2}
handleChangeMail={this.handleChangeMail}
handleSubmit={this.handleSubmit}
/>
</div>
</div>
);
}
}

Here {this.props.permUpdated ? {permissionsPrint} : null} I don't think you need curly braces around permissionsPrint it should be :
{this.props.permUpdated ? permissionsPrint : null}
{permissionsPrint} this is the same as {permissionsPrint: permissionsPrint} which is an Object with same key and value names, where value is a variable of the same name as of key name.

Se issue was that when the modal was closed, permUpdated state was kept as true, so I had to set it to false on close
closeModal() {
console.log("CLOOOOOOSSSSINNGGGGGGGGGGGGG")
this.setState({
showModal: false,
permUpdated: false
}, () => {
// console.log("clicked closeModal")
// console.log(this.state.showModal)
});
}

Related

How can I delete a React Class Component from its Parent Class OnClick of Delete button

I have a project that I'm using PHP and React on it, it consists on making a CRUD system with AJAX Calls.
This is the interface I made so far
The problem is that when I click delete, the request is sent and the data row is deleted, but when the parent refreshes it doesn't remove the deleted item from the list.
This is the User class component (for each row):
class User extends React.Component {
constructor(props) {
super(props);
this.state = {
isEditing: false,
id: this.props.id,
username: this.props.username,
email: this.props.email,
password: this.props.password,
refID: '',
refUsername: '',
refEmail: '',
refPassword: ''
};
this.refID = this.props.id;
this.refUsername = this.props.username;
this.refEmail = this.props.email;
this.refPassword = this.props.password;
this.handleDelete = this.handleDelete.bind(this);
}
handleEdit() {
this.setState({isEditing: true});
}
handleDelete() {
$.ajax({
type: 'POST',
url: 'http://localhost/CRUD/server/api/delete.php',
data: {
id: this.state.id
},
success: function(response) {
toast.success('User deleted successfully!', {
position: "bottom-left",
autoClose: 3000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
icon: CheckCircleIcon
});
console.log(response);
// Remove the user from the list.
},
error: function(jqXHR, textStatus, errorThrown){
toast.error('Error deleting user!', {
position: "bottom-left",
autoClose: 3000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
icon: CheckCircleIcon
});
console.log(' response: ', jqXHR, textStatus, errorThrown)
return false;
}
})
this.props.rerender();
}
handleCancel() {
this.setState({
isEditing: false
});
}
handleSave() {
$.ajax({
type: 'POST',
url: 'http://localhost/CRUD/server/api/update.php',
data: {
id: this.refID,
username: this.refUsername,
email: this.refEmail,
password: this.refPassword
},
success: function() {
toast.success("Successfully updated the user information.",{
position: "bottom-left",
autoClose: 3000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
icon: CheckCircleIcon
});
},
error: function(jqXHR, textStatus, errorThrown){
toast.error("Error updating the user information.");
console.log(' response: ', jqXHR, textStatus, errorThrown)
}
})
// Update the state with the new values. As well as changing the display back to the original.
this.setState({isEditing: false, id: this.refID, username: this.refUsername, email: this.refEmail, password: this.refPassword});
}
render() {
let DisplayMode, EditMode;
DisplayMode = (
<div className="Row">
<div className="Cell C1" id='DataID'>
<p>{this.state.id}</p>
</div>
<div className="Cell C2" id='DataUsername'>
<p>{this.state.username}</p>
</div>
<div className="Cell C3" id='DataEmail'>
<p>{this.state.email}</p>
</div>
<div className="Cell C4" id='DataPassword'>
<p>{this.state.password}</p>
</div>
<div className="Cell C5" id='DataActions'>
<div className="Button Secondary Edit">
{/* On click of the Edit button, switch the cells with inputs, and change the buttons to save button*/}
<EditIcon onClick={() => this.handleEdit()} />
</div>
<div className="Button Primary Delete">
<DeleteIcon onClick={() => this.handleDelete()} />
</div>
</div>
</div>
);
EditMode = (
<div className="Row EditMode">
<div className="Cell C1" id="DataID">
<p>{this.props.id}</p>
</div>
<div className="Cell C2 Editing" id="DataUsername">
<input type='text' placeholder='Edit username' defaultValue={this.state.username} onChange={e => {this.refUsername = e.target.value}}/>
</div>
<div className="Cell C3 Editing" id="DataEmail">
<input type='text' placeholder='Edit e-mail' defaultValue={this.state.email} onChange={e => {this.refEmail = e.target.value}}/>
</div>
<div className="Cell C4 Editing" id="DataPassword">
<input type='text' placeholder='Edit password' defaultValue={this.state.password} onChange={e => {this.refPassword = e.target.value}}/>
</div>
<div className="Cell C5" id="DataActions">
<div className="Button Secondary Edit">
<CancelIcon onClick={() => this.handleCancel()}/>
</div>
<div className="Button Primary Delete">
<SaveIcon onClick= {() => this.handleSave()} />
</div>
</div>
</div>
);
const CurrentMode = () => {
return(
this.state.isEditing ? EditMode : DisplayMode
)
};
return (
<>
<CurrentMode/>
</>
);
};
};
And this is the code of the Parent class (For the entire table):
class UsersListView extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoaded: false,
error: null,
users: [],
counter: 0,
isLoading: false
};
this.handleRefresh = this.handleRefresh.bind(this);
};
rerender = () => {
this.forceUpdate();
};
forceUpdate = () => {
this.setState((state) => ({
counter: state.counter + 1,
isLoaded: false
}));
};
componentDidMount() {
this.handleRefresh();
}
componentDidUpdate() {
console.log("Updated");
() => {
this.handleRefresh();
}, [this.state.users]
};
handleRefresh() {
fetch("http://localhost/CRUD/server/api/read.php")
.then(res => res.json())
.then((result) => {
this.setState({
isLoading: false,
isLoaded: true,
users: result
});
},
(error) => {
this.setState({
isLoading: false,
isLoaded: false,
error
});
console.log(error)
}
)
this.forceUpdate();
}
render() {
return this.state.isLoaded ?
// If the data is loaded, render the users.
this.state.users.map((user) => {
return (
<User
rerender={this.rerender}
key={user.id}
id={user.id}
username={user.username}
email={user.email}
password={user.password}
/>
)
})
// If the data is not loaded, display a loading message.
: (
<div className='User-Loading'>
<div className='Loading'>
<div className="loader">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
<div className='User-Loading-Text'>
<h3>Loading...</h3>
<p>Taking too long? Check the API link if is connected.</p>
<div className='button' onClick={this.handleRefresh}>
<RefreshIcon/>
Retry
</div>
</div>
</div>
)
}
}
export default UsersListView;
Heading ##UPDATES:
I brought the handleDelete() function to the parent class, and I called it using the props of the child component, and it worked.
I also updated the state of the parent to solve the main issue of removing the row after each delete.

Fetch data from API when form's search button clicked and show data on another page in React JS

I am developing a React JS web application where I have a form with four select fields (Make, Model, Min price and Max price) and a Search button. The data for search results will be fetched from API according to the selection of options. I want to show that data on another page in a card (page route path: /search) when user clicked on search button. I am using react router. The API url/end point is https://mysterious-journey-51969.herokuapp.com/api/search-vehicle/?q=mercedes&m=sprinter&pf=0&pt=100000 where "q" field matches Vehicle Make, "m" field matches Model, "pf" field matches Min Price, "pt" field matches Max Price. How I can do that?
Here is my Form component code:
import React, { Component } from 'react';
import { Form, FormGroup, Input } from 'reactstrap';
import { veh_data } from '../shared/vehicle_make_and_models';
const defaultValues = [
{ value: 0, text: 0, key: 1 },
{ value: 500, text: 500, key: 2 },
{ value: 1000, text: 1000, key: 3 },
{ value: 1500, text: 1500, key: 4 },
{ value: 2000, text: 2000, key: 5 },
{ value: 2000, text: 2000, key: 6 }
];
const MIN_TITLE = { selected: true, disabled: true, text: 'Min Price' };
const MAX_TITLE = { selected: true, disabled: true, text: 'Max Price' };
class ImgAndForm extends Component {
constructor(props) {
super(props);
this.handleSearch = this.handleSearch.bind(this);
this.keyToOption = this.keyToOption.bind(this);
this.renderOptions = this.renderOptions.bind(this);
this.handleModelChange = this.handleModelChange.bind(this);
this.state = {
minData: [MIN_TITLE, ...defaultValues],
maxData: [MAX_TITLE, ...defaultValues],
minValue: null,
maxValue: null,
modelSelected: null
};
}
renderOptions(data) {
return data.map(datum => {
// this allows us to indicate whether we are selecting or disabling
const selected = datum.selected || false;
const disabled = datum.disabled || false;
return (
<option key={datum.key} value={datum.value} selected={selected} disabled={disabled}>
{datum.text}
</option>
);
});
}
handleModelChange(event) {
console.log(event.target.value);
this.setState({ modelSelected: event.target.value });
}
handleSearch(event) {
alert("Search button clicked");
}
keyToOption(key) {
return key.split("-")
.map(word => word.slice(0, 1).toUpperCase() + word.slice(1))
.join(" ");
}
handleMinSelect = event => {
const value = event.target.value;
const newMaxValues = [];
defaultValues.forEach(datum => {
if (datum.value >= Number.parseInt(value, 10)) {
newMaxValues.push(datum);
}
});
this.setState({
maxData: [MAX_TITLE, ...newMaxValues],
minValue: value
});
};
handleMaxSelect = event => {
const value = event.target.value;
this.setState({ maxValue: value });
};
render() {
const vehicles = veh_data.reduce((acc, veh, i) => {
let make = Object.keys(veh)[0],
vehModels = veh[make];
return {
makes: [
...acc.makes,
<option key={make + i} value={make}>{this.keyToOption(make)}</option>
],
models: {
...acc.models,
[make]: vehModels.map((model, i) => {
return (
<option key={make + model + i} value={model}>
{this.keyToOption(model)}
</option>
);
})
}
};
}, { makes: [], models: [] });
const selectedModels =
this.state.modelSelected && this.state.modelSelected.length ? (
vehicles.models[this.state.modelSelected]
) : (
<option value="">Model (select make first)</option>
);
return (
<div>
<header className="headerbg d-flex">
<div className="container my-auto">
<div className="row">
<div className="offset-1 col-10 offset-lg-0 col-lg-4">
<div id="search-form-div" className="container">
<div className="row">
<div className="col-12 my-4">
<h3>Search</h3>
<Form onSubmit={this.handleSearch}>
<FormGroup>
<Input
onChange={e => this.handleModelChange(e)}
type="select"
name="q"
id="q"
>
<option value="">Make</option>
{vehicles.makes}
</Input>
</FormGroup>
<FormGroup>
<Input type="select" name="m" id="m">
{selectedModels}
</Input>
</FormGroup>
<FormGroup>
<Input type="select"
name="pf"
id="pf"
value={this.state.minValue}
onChange={this.handleMinSelect}>
{this.renderOptions(this.state.minData)}
</Input>
</FormGroup>
<FormGroup>
<Input
type="select"
name="pt"
id="pt"
value={this.state.maxValue}
onChange={this.handleMaxSelect}>
{this.renderOptions(this.state.maxData)}
</Input>
</FormGroup>
<FormGroup>
<Input type="submit" name="search" id="search" className="btn btn-primary" value="Search" />
</FormGroup>
</Form>
</div>
</div>
</div>
</div>
</div>
</div>
</header>
</div>
);
}
}
export default ImgAndForm;
Here is my Search result component code:
import React, { Component } from 'react';
import Smallheader from './SmallHeader';
import { Card, CardImg, CardTitle, CardSubtitle } from 'reactstrap';
class SearchResult extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<div>
<Smallheader />
<div className="my-5">
<div className="container text-center" id="contactContainer">
<div className="row">
<div className="col-lg-12 mx-auto">
<h2 className="text-center">Search Results</h2>
<hr className="my-4 thick-hr" />
</div>
</div>
<div className="row">
<div className="col-6 col-lg-3 mt-4">
<Card>
<a href="#">
<CardImg src="" className="img-fluid" />
<CardTitle>Title Here</CardTitle>
<CardSubtitle>Price Here</CardSubtitle>
</a>
</Card>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default SearchResult;
Here is a working solution...
https://codesandbox.io/s/lrv2w3qxlq?moduleview=1
I've imported your SearchResults component and put it directly below your ImgAndForm, but you can move it anywhere in that render function.
For this specific situation you would need a way to render this on a new 'page' you would need a way to manage shared application state, like Redux or at least a container component as #MikeZinn mentioned, but to do that properly would require as significant amount of work to implement the routing and re-architect your entire program. (If you want I can show you a small hack to produce the same result without that for now, but I'd advise looking into a more permanent solution.)
Since the SearchResults component can be 'stateless' I removed the constructor function, but I left it as a class for now because this component will likely need state eventually.
I added the axios library to fetch the data from the API, but any other XHR module already used in your program will do.
NOTE: Since the specific API endpoints that your form is currently able to query are unavailable, I've hard coded the 'mercedes' example you provided, but the program will log both 'realQuery' and 'dummyQuery' so you see that it is producing the correct query structure for whenever you fix that.
Form Component
import React, { Component } from "react";
import { Form, FormGroup, Input } from "reactstrap";
// import { veh_data } from '../shared/vehicle_make_and_models';
import SearchResult from "./result";
import axios from "axios";
const veh_data = [
{ "alfa-romeo": ["145", "90", "Alfa 6", "Alfasud"] },
{ "aston-martin": ["15", "2-Litre", "AM Vantage", "Atom", "Cygnet", "DB2"] },
{ audi: ["100", "200", "A1", "A2", "A3", "A4", "A5", "A6", "A7"] }
];
const defaultValues = [
{ value: 0, text: 0, key: 1 },
{ value: 500, text: 500, key: 2 },
{ value: 1000, text: 1000, key: 3 },
{ value: 1500, text: 1500, key: 4 },
{ value: 2000, text: 2000, key: 5 },
{ value: 2000, text: 2000, key: 6 }
];
const MIN_TITLE = { selected: true, disabled: true, text: "Min Price" };
const MAX_TITLE = { selected: true, disabled: true, text: "Max Price" };
class ImgAndForm extends Component {
constructor(props) {
super(props);
this.handleSearch = this.handleSearch.bind(this);
this.keyToOption = this.keyToOption.bind(this);
this.renderOptions = this.renderOptions.bind(this);
this.handleModelChange = this.handleModelChange.bind(this);
this.state = {
minData: [MIN_TITLE, ...defaultValues],
maxData: [MAX_TITLE, ...defaultValues],
minValue: "",
maxValue: "",
modelSelected: "",
makeSelected: "",
searchResults: ""
};
}
renderOptions(data) {
return data.map(datum => {
// this allows us to indicate whether we are selecting or disabling
const selected = datum.selected || false;
const disabled = datum.disabled || false;
return (
<option
key={datum.key}
value={datum.value}
selected={selected}
disabled={disabled}
>
{datum.text}
</option>
);
});
}
handleModelChange(event) {
console.log(event.target.value);
this.setState({ modelSelected: event.target.value });
}
handleMakeChange(event) {
console.log(event.target.value);
this.setState({ makeSelected: event.target.value });
}
async handleSearch(event) {
event.preventDefault();
alert("Search button clicked");
let { makeSelected, modelSelected, minValue, maxValue } = this.state;
let realQuery =
"https://mysterious-journey-51969.herokuapp.com/api/search-vehicle/?" +
`q=${makeSelected.split("-").join("")}` +
`&m=${modelSelected.split("-").join("")}` +
`&pf=${minValue}` +
`&pt=${maxValue}`;
let dummyQuery =
"https://mysterious-journey-51969.herokuapp.com/api/search-vehicle/?q=mercedes&m=sprinter&pf=0&pt=100000";
console.log("realQuery (was not run)", realQuery);
console.log("dummyQuery (was run)", dummyQuery);
let res = await axios.get(dummyQuery).catch(err => console.log(err));
console.log("res", res.data);
if (res && res.data) {
this.setState(prevState => {
return {
...prevState,
searchResults: res.data
};
});
}
}
keyToOption(key) {
return key
.split("-")
.map(word => word.slice(0, 1).toUpperCase() + word.slice(1))
.join(" ");
}
handleMinSelect = event => {
const value = event.target.value;
const newMaxValues = [];
defaultValues.forEach(datum => {
if (datum.value >= Number.parseInt(value, 10)) {
newMaxValues.push(datum);
}
});
this.setState({
maxData: [MAX_TITLE, ...newMaxValues],
minValue: value
});
};
handleMaxSelect = event => {
const value = event.target.value;
this.setState({ maxValue: value });
};
render() {
const vehicles = veh_data.reduce(
(acc, veh, i) => {
let make = Object.keys(veh)[0],
vehModels = veh[make];
return {
makes: [
...acc.makes,
<option key={make + i} value={make}>
{this.keyToOption(make)}
</option>
],
models: {
...acc.models,
[make]: vehModels.map((model, i) => {
return (
<option key={make + model + i} value={model}>
{this.keyToOption(model)}
</option>
);
})
}
};
},
{ makes: [], models: [] }
);
const selectedModels =
this.state.makeSelected && this.state.makeSelected.length ? (
vehicles.models[this.state.makeSelected]
) : (
<option value="">Model (select make first)</option>
);
return (
<div>
<header className="headerbg d-flex">
<div className="container my-auto">
<div className="row">
<div className="offset-1 col-10 offset-lg-0 col-lg-4">
<div id="search-form-div" className="container">
<div className="row">
<div className="col-12 my-4">
<h3>Search</h3>
<Form onSubmit={this.handleSearch}>
<FormGroup key={1}>
<Input
onChange={e => this.handleMakeChange(e)}
type="select"
name="q"
id="q"
>
<option value="">Make</option>
{vehicles.makes}
</Input>
</FormGroup>
<FormGroup key={2}>
<Input
onChange={e => this.handleModelChange(e)}
type="select"
name="m"
id="m"
>
{selectedModels}
</Input>
</FormGroup>
<FormGroup key={3}>
<Input
type="select"
name="pf"
id="pf"
value={this.state.minValue}
onChange={this.handleMinSelect}
>
{this.renderOptions(this.state.minData)}
</Input>
</FormGroup>
<FormGroup key={4}>
<Input
type="select"
name="pt"
id="pt"
value={this.state.maxValue}
onChange={this.handleMaxSelect}
>
{this.renderOptions(this.state.maxData)}
</Input>
</FormGroup>
<FormGroup key={5}>
<Input
type="submit"
name="search"
id="search"
className="btn btn-primary"
value="Search"
/>
</FormGroup>
</Form>
<SearchResult results={this.state.searchResults} />
</div>
</div>
</div>
</div>
</div>
</div>
</header>
</div>
);
}
}
export default ImgAndForm;
Results Component
import React, { Component } from "react";
// import Smallheader from './SmallHeader';
import { Card, CardImg, CardTitle, CardSubtitle } from "reactstrap";
class SearchResult extends Component {
renderResults() {
let { results } = this.props;
console.log("results", results);
if (results && results.length) {
return results.map(({ price, text, title, remote_image }, i) => {
return (
<Card key={"card-" + i}>
<a href="#">
<CardImg src={remote_image} className="img-fluid" />
<CardTitle>{title}</CardTitle>
<CardSubtitle>{price}</CardSubtitle>
</a>
</Card>
);
});
}
}
render() {
return (
<div>
{/* <Smallheader /> */}
<div className="my-5">
<div className="container text-center" id="contactContainer">
<div className="row">
<div className="col-lg-12 mx-auto">
<h2 className="text-center">Search Results</h2>
<hr className="my-4 thick-hr" />
</div>
</div>
<div className="row">
<div className="col-6 col-lg-3 mt-4">{this.renderResults()}</div>
</div>
</div>
</div>
</div>
);
}
}
export default SearchResult;
This is exactly the type of problem Redux Solves without using Redux you will need to store the state on a shared parent component. For example,
class Search extends Component {
state = {
searchResult: null,
};
handleSearch = searchResult => {
this.setState({
searchResult,
});
}
render(){
const { searchResult, } = this.state;
if(searchResult === null){
return (
<ImgAndForm handleSearch={this.handleSearch} />
);
}
return (
<SearchResult searchResult={searchResult} />
);
}
}

React - set state on unmounted component

I tried a lot of ways to make it works but it didn't. Probably I don't understand the React idea or I missed something. My child via callback try to modify the global/parent state and then I got the well known error.
index.js:1452 Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
I try to use variable _isMounted so before setState I did:
if (this.isMounted()) {
this.setState({...});
}
Unfortunately it doesn't work.
The problem is when the function this.handleChangeAlgorithms is invoked from the child.
My code
class ConfigurationForm extends Component {
constructor(props) {
super(props);
this.state = {
choosenAlgorithm: null,
htmlType: null,
customHtml: null,
isOpenModal: false,
generatedHTMlConfig: {
headers: 1,
paragraphs: 1,
buttons: 1,
links: 1,
inputs: 1,
images: 1,
},
};
}
handleChangeAlgorithms = event => {
let algorithmName = event.target.value;
let newChoosenAlgorithm = this.props.algorithms.filter(
e => e.value === algorithmName,
)[0];
// this.setState({choosenAlgorithm: newChoosenAlgorithm});
this.props.callbackConfigurationForm(algorithmName);
};
handleChangeHtmlType(event) {
let newHtmlType = event.target.value;
console.log(newHtmlType);
if (newHtmlType === "default") {
this.setState({ isOpenModal: true });
} else {
this.setState({ htmlType: newHtmlType });
}
}
handleUserFile(event) {
let file = event.target.files[0];
this.setState({ customHtml: file });
this.props.callbackConfigurationFormPreview(file);
}
handleSubmit(event) {
event.preventDefault();
let htmlContent = null;
let htmltypeKey = Object.keys(HtmlType).find(
key => HtmlType[key] === HtmlType.default,
);
console.log(htmltypeKey);
// if (this.state.htmlType === 'default'){
// htmlContent = this.loadTemplate();
htmlContent = generateHTML(this.state.generatedHTMlConfig);
console.log(htmlContent);
// } else {
// htmlContent=this.state.customHtml;
// }
let config = {
algorithm: this.state.choosenAlgorithm,
html: htmlContent,
};
console.log(config);
this.props.callbackConfigurationForm(config);
}
loadTemplate() {
let loadedTemplate = null;
let file = "html-templates/example.html";
let request = new XMLHttpRequest();
request.open("GET", file, false);
request.send(null);
if (request.status === 200) {
loadedTemplate = request.responseText;
}
return loadedTemplate;
}
renderHtmlTypesList = () => {
let list = [];
for (const htmlKey of Object.keys(HtmlType)) {
list.push(
<option key={htmlKey} value={htmlKey}>
{HtmlType[htmlKey]}
</option>,
);
}
return list;
};
generateHTMLFromPopup() {
this.setState({ isOpenModal: false });
}
changeHeaders(event, type) {
console.log(type);
console.log(event.target.value);
let prevConfig = { ...this.state.generatedHTMlConfig };
switch (type) {
case "Headers":
prevConfig.headers = event.target.value;
this.setState({ generatedHTMlConfig: prevConfig });
break;
case "Paragraphs":
prevConfig.paragraphs = event.target.value;
this.setState({ generatedHTMlConfig: prevConfig });
break;
case "Buttons":
prevConfig.buttons = event.target.value;
this.setState({ generatedHTMlConfig: prevConfig });
break;
case "Links":
prevConfig.links = event.target.value;
this.setState({ generatedHTMlConfig: prevConfig });
break;
case "Inputs":
prevConfig.inputs = event.target.value;
this.setState({ generatedHTMlConfig: prevConfig });
break;
case "Images":
prevConfig.images = event.target.value;
this.setState({ generatedHTMlConfig: prevConfig });
break;
}
}
render() {
return (
<Row>
{/* todo 12.11.2018 extract to another component! */}
<Modal
open={this.state.isOpenModal}
header="Generate Html"
actions={
<div>
<Button
modal="close"
waves="light"
className="red lighten-2"
>
Cancel
</Button>
<Button
modal="close"
waves="light"
className="blue"
onClick={this.generateHTMLFromPopup.bind(this)}
>
<Icon left>build</Icon>Generate
</Button>
</div>
}
>
<p>Choose HTML elements for generated HTML.</p>
<Input
type="number"
label="Headers"
value={this.state.generatedHTMlConfig.headers}
onChange={e => this.changeHeaders(e, "Headers")}
/>
<Input
type="number"
label="Paragraphs"
value={this.state.generatedHTMlConfig.paragraphs}
onChange={e => this.changeHeaders(e, "Paragraphs")}
/>
<Input
type="number"
label="Buttons"
value={this.state.generatedHTMlConfig.buttons}
onChange={e => this.changeHeaders(e, "Buttons")}
/>
<Input
type="number"
label="Links"
value={this.state.generatedHTMlConfig.links}
onChange={e => this.changeHeaders(e, "Links")}
/>
<Input
type="number"
label="Inputs"
value={this.state.generatedHTMlConfig.inputs}
onChange={e => this.changeHeaders(e, "Inputs")}
/>
<Input
type="number"
label="Images"
value={this.state.generatedHTMlConfig.images}
onChange={e => this.changeHeaders(e, "Images")}
/>
</Modal>
<h2>Algorithm</h2>
<Row>
<Input
s={12}
type="select"
label="Select algorithm"
defaultValue=""
onChange={this.handleChangeAlgorithms}
>
<option value="" disabled>
Choose an algorithm
</option>
{this.props.algorithms.map(item => (
<option key={item.value} value={item.value}>
{item.name}
</option>
))}
</Input>
</Row>
{this.state.choosenAlgorithm ? (
<Collapsible popout>
<CollapsibleItem header="Details" icon="notes">
<ol>
{this.state.choosenAlgorithm.details.steps.map(
(step, index) => (
<li key={index}>{step}</li>
),
)}
</ol>
</CollapsibleItem>
</Collapsible>
) : null}
<h2>HTML to obfuscate</h2>
<Row>
<Input
s={12}
type="select"
label="HTML type"
defaultValue=""
onChange={this.handleChangeHtmlType}
>
<option value="" disabled>
Choose HTML
</option>
{this.renderHtmlTypesList()}
</Input>
</Row>
{this.state.htmlType === "custom" ? (
<Row>
<Input
type="file"
label="File"
onChange={this.handleUserFile}
/>
</Row>
) : null}
<div className="center-align">
<Button type={"button"} onClick={this.handleSubmit}>
Process<Icon left>autorenew</Icon>
</Button>
</div>
</Row>
);
}
}
class App extends Component {
_isMounted = false;
constructor(props) {
super(props);
this.state = {
isMounted: false,
//export to enum
algorithms: [
{
name: "Html to Javascript",
value: "1",
details: {
steps: [
"Split HTML file line by line.",
"Replace white characters.",
"Create function which add lines to document using document.write function.",
],
},
},
{
name: "Html to Unicode characters",
value: "2",
details: {
steps: [
"Create js function encoding characters to Unicode characters.",
"Create decoding function.",
"Add output from decoding function to HTML.",
],
},
},
{
name: "Html to escape characters",
value: "3",
details: {
steps: [
"Change endcoding using escape javascript function.",
"Decode using unescape javascript function.",
"Add element to HTML.",
],
},
},
{
name:
"Using own encoding and decoding function. [NOT IMPLEMENTED YET]",
value: "4",
details: {
steps: [
"Encode HTML using own function.",
"Save encoded content into js variable.",
"Decode using own decoding function.",
"Add element to HTML document.",
],
},
},
{
name: "Combine above methods [NOT IMPLEMENTED YET]",
value: "5",
details: {
steps: ["To be done..."],
},
},
],
previewHtml: null,
obfuscationConfig: null,
activeTab: 1,
};
}
componentDidMount() {
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}
processDataFromConfigurationForm = config => {
console.info(config);
if (this._isMounted) {
this.setState({
test: Date.now(),
// obfuscationConfig: config,
// doObfuscation: true,
// activeTab:3,
// previewHtml: config.html
});
}
};
render() {
return (
<div>
<header className="App">
<h1>HTML obfuscator</h1>
</header>
<Tabs>
<Tab
title="Configuration"
active={this.state.activeTab === 1}
>
<ConfigurationForm
algorithms={this.state.algorithms}
config={this.state.obfuscationConfig}
callbackConfigurationForm={
this.processDataFromConfigurationForm
}
/>
</Tab>
<Tab
title="HTML Preview"
active={this.state.activeTab === 2}
disabled={!this.state.previewHtml}
>
<HTMLPreview previewHtml={this.state.previewHtml} />
</Tab>
<Tab
title="Result"
active={this.state.activeTab === 3}
disabled={!this.state.obfuscationConfig}
>
{this.state.obfuscationConfig ? (
<ObfuscationOutput
config={this.state.obfuscationConfig}
/>
) : null}
</Tab>
</Tabs>
</div>
);
}
}
export default App;
The error
Try to add a constructor to your parent class, indeed, you can initialize the component state before the first modification:
class App extends Component {
constructor(props) {
super(props);
this.state = {
test1: Date.now()
};
}
// here you can update the state as you want, for example
foobar(event) {
this.setState({test1: event.target.value });
}
}
Hope it can help you...
Setting the state inside of the lifecycle method componentDidMount() should do the trick.
Oh I didn't see that someone already suggested that, glad you got it working

not able to display material-ui dropdown with props values

I am trying to incorporate material-ui DropDown element into my page but I am getting map errors
My page code
import React, {Component} from 'react';
import {Button, Dropdown, Form} from 'semantic-ui-react'
import SimpleReactValidator from 'simple-react-validator';
import cookie from 'react-cookies'
import {Redirect} from 'react-router-dom'
const jsonData = require('../../../../../jsonData/data.json');
const dummyData = require('../../../../../jsonData/dummy.json');
import auth, {
axiosInst as axios,
removeElement,
addElement,
beforeSubmiteShaker,
afterSubmiteShaker
} from "../../../../helper";
import QuestionsFilters from '../../../../components/QuestionsFilters'
import DropDown from '../../../../components/ui/Select/Select'
const items = [
{value: 1, primaryText: 'Dhaval'},
{value: 2, primaryText: 'Dhavalu'},
{value: 3, primaryText: 'Dhavalaa'},
]
class RegisterCandidate extends Component {
constructor(props) {
super(props);
this.state = {
firstname: "",
lastname: "",
mobile_no: "",
email: "",
password: "",
city_id: "",
preference_list: [],
medium: "", //how did u find us
is_looking_for: "",
affiliate: "",
revert: "",
emailExist: "",
mobileExist: "",
isLogin: false,
historyRoute: "",
};
this.validator = new SimpleReactValidator();
}
componentDidMount() {
this.props.fetchCities()
this.props.fetchPreferences()
}
change(e) {
this.setState({
[e.target.name]: e.target.value
})
};
dropDownChange(e, {value, name}) {
this.setState({
[name]: value
})
}
emailcheck(e) {
if (this.validator.fieldValid('email')) {
let uri = '/email/exists';
axios.get(uri, {
params: {email: this.state.email}
})
.then((response) => {
this.setState({emailExist: response.data});
if (response.data.status != 'Failed') {
removeElement("p[id='serv-error email-exist']")
return axios.get('/droppedregistrations/add', {
params: {email: this.state.email}
}).then((response) => {
console.log("Email Dropped " + response.data)
}).catch((error) => {
console.log('failed')
})
} else {
removeElement("p[id='serv-error email-exist']")
addElement("#email", "serv-error email-exist", "This email already exists.")
}
})
.catch(error => {
console.log('error Email')
})
}
}
mobilecheck(e) {
if (this.state.mobile_no != isNaN && this.state.mobile_no.length == 10) {
let uri = '/number/exists';
axios.get(uri, {
params: {mobile_no: this.state.mobile_no}
}).then((response) => {
this.setState({mobile_noExist: response.data});
if (response.data.status != 'Failed') {
removeElement("p[id='serv-error mobile-exist']")
return axios.get('/droppedregistrations/add', {
params: {mobile_no: this.state.mobile_no}
}).then((response) => {
console.log("mobile dropped " + response.data)
}).catch((error) => {
console.log('failed')
})
} else {
removeElement("p[id='serv-error mobile-exist']")
addElement("#mobile_no", "serv-error mobile-exist", "This mobile already exists.")
}
})
.catch(error => {
console.log('You are experiencing slow internet please be patient and try again later')
})
}
}
addUserSubmit(e) {
e.preventDefault();
beforeSubmiteShaker("s_reg_submit", "shaker")
if (this.validator.allValid()) {
const userDetails = {//add data to a donstant to pot
firstname: this.state.firstname,
lastname: this.state.lastname,
email: this.state.email,
password: this.state.password,
mobile_no: this.state.mobile_no,
city_id: this.state.city_id,
medium: this.state.medium,
is_looking_for: this.state.is_looking_for,
preference_list: this.state.preference_list,
affiliate: this.state.affiliate
}
let uri = '/register/candidate';
axios.post(uri, userDetails)
.then((response) => {
this.setState({revert: response.data});
const gotBack = this.state.revert
if (gotBack.status === 'Success') {
cookie.remove('token')
cookie.save('token', gotBack.token, {path: '/'})
if (cookie.load('token')) {
this.setState({isLogin: true})
}
else {
console.log('Something went wrong while redirect.')
}
}
})
.catch(error => {
if (error.response.status === 422) {
afterSubmiteShaker("s_reg_submit", "shaker")
$.each(error.response.data.errors, function (index, value) {
var errorDiv = '#' + index;
$(errorDiv).after("<p id='serv-error' class='validation-message'>" + value + "</p>");
});
}
})
} else {
this.validator.showMessages();
this.forceUpdate();
afterSubmiteShaker("s_reg_submit", "shaker")
}
}
render() {
const {
firstname,
lastname,
mobile_no,
email,
password,
city_id,
preference_list,
medium,
is_looking_for,
affiliate,
isLogin
} = this.state;
//to get the items in level of education
let looking_for = jsonData.looking_for;
var looking_fors = []
for (let i = 0; i < looking_for.length; i++) {
looking_fors.push(looking_for[i]['value']);
}
if (isLogin) {
return <Redirect to='/register/candidate/step2'/>;
} else {
return (
<section className="container mt-3">
{/*one click register section*/}
<div className="row p-3">
<div className="col-md-6">
<div className="card text-center">
<div className="card-body">
<h3>Register Using</h3>
<br/>
<a href="/" className={"btn btn-block btn-lg btn-info"}> Register with Facebook</a>
<a href="/google/redirect" className={"btn btn-block btn-lg btn-danger"}> Register
with Google</a>
<br/>
</div>
</div>
</div>
{/*end of one click register section*/}
{/*manuel register section*/}
<div className="col-md-6">
<div id="shaker" className="card">
<div className="card-body">
<h5 className="card-title">Register Candidate</h5>
<Form onSubmit={this.addUserSubmit.bind(this)}>
<Form.Field>
<label>First Name*</label>
<input name="firstname" id="firstname" value={firstname}
onChange={e => this.change(e)} type="text"
placeholder='Enter First Name*'/>
{this.validator.message('first name', firstname, 'required|alpha')}
</Form.Field>
<Form.Field>
<label>Last Name*</label>
<input name="lastname" id="lastname" value={lastname}
onChange={e => this.change(e)} type="text"
placeholder='Enter Last Name'/>
{this.validator.message('last name', lastname, 'required|alpha')}
</Form.Field>
<Form.Field>
<label>Mobile No*</label>
<input name="mobile_no" id="mobile_no" value={mobile_no}
onBlur={this.mobilecheck.bind(this)} onChange={e => this.change(e)}
type="text" placeholder='Enter mobile_no'/>
{this.validator.message('mobile no.', mobile_no, 'required|phone|min:10|max:10')}
</Form.Field>
<Form.Field>
<label>Email*</label>
<input name="email" id="email" value={email}
onBlur={this.emailcheck.bind(this)} onChange={e => this.change(e)}
type="text" placeholder='Enter Email'/>
{this.validator.message('email', email, 'required|email')}
</Form.Field>
<Form.Field>
<label>Password*</label>
<input name="password" id="password" value={password}
onChange={e => this.change(e)} type="password"
placeholder='Enter Password'/>
{this.validator.message('password', password, 'required|min:6')}
</Form.Field>
<Form.Field>
<label>City*</label>
<Dropdown placeholder='Select City' id="city_id" name="city_id" search
selection options={this.props.city_opt} value={city_id}
onChange={this.dropDownChange.bind(this)}/>
{this.validator.message('city', city_id, 'required|gt:0')}
</Form.Field>
<Form.Field>
<label>Preference*</label>
<Dropdown placeholder='Select Preference' id="preference_list"
name="preference_list" search multiple selection
options={this.props.preference_list_opt} value={preference_list}
onChange={this.dropDownChange.bind(this)}/>
{this.validator.message('preference', preference_list, 'required|max:3', false, {max: 'Maximum 3 preferences allowed'})}
</Form.Field>
<Form.Field>
<label>How did you find us?*</label>
<Dropdown placeholder='Please Select' id="medium" name="medium" search
selection options={jsonData.medium} value={medium}
onChange={this.dropDownChange.bind(this)}/>
{this.validator.message('medium', medium, 'required')}
</Form.Field>
<Form.Field>
<label>What are you looking for?*</label>
<Dropdown placeholder='Please Select' id="is_looking_for"
name="is_looking_for" search selection
options={jsonData.looking_for} value={is_looking_for}
onChange={this.dropDownChange.bind(this)}/>
{this.validator.message('', is_looking_for, 'required|in:' + looking_fors)}
</Form.Field>
<Form.Field>
<label>affiliate Code</label>
<input name="affiliate" value={affiliate} onChange={e => this.change(e)}
type="text" placeholder='Enter preference_list'/>
</Form.Field>
<Button id="s_reg_submit" className={"btn btn-block btn-lg"}
type='submit'>Submit</Button>
</Form>
<QuestionsFilters/>
</div>
</div>
</div>
{/*end of manuel register section*/}
<DropDown items={items}/>
</div>
</section>
)
}
}
}
export default RegisterCandidate;
my DropDown component code
import React, {Component} from 'react';
import DropDownMenu from 'material-ui/DropDownMenu';
import MenuItem from 'material-ui/MenuItem';
const styles = {
customWidth: {
width: 200,
},
};
class DropDownMenuSimpleExample extends Component {
constructor(props) {
super(props);
this.state = {value: 1, items: ['abcd', 'efgh']};
}
handleChange(event, index, value) {
return this.setState({value})
}
render() {
let propItems = this.props.items
return (
<DropDownMenu
value={this.state.value}
onChange={this.handleChange.bind(this)}
style={styles.customWidth}
autoWidth={false}
>
{(propItems !== null || propItems != 'undefined' ) ? (propItems).map((item, index) => <MenuItem key={index} value={1}
primaryText="Custom height"/>) : (this.state.items).map((item, index) =>
<MenuItem key={index} value={1} primaryText="Custom width"/>)}
</DropDownMenu>
);
}
}
export default DropDownMenuSimpleExample
it is telling me that It cannot call map on null and in the first render the value of props shows undefined but then it is defined so not able to get to render the DropDown element properly..
any help will be highly appreciated
Please Note :- with just the state I get the proper dropdown but the issue happens only when I am passing props as data for the dropdown

OnChange event handlers don't get called when I'm updating a form

I have a form that should update my applications state as a user types in the input field and in the textarea. The input and textarea call their event handlers through onChange={event handler}. For some reason when I type in either field the event handlers don't get called at all. The input and textarea fields seem to work fine outside of a form though.
import React, { Component, PropTypes } from 'react';
import { reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import LogoutHeader from './LogoutHeader';
import { fetchTodo, updateTodo, deleteTodo } from '../actions/index';
import { Link } from 'react-router';
class ShowTodo extends Component {
static contextTypes = {
router: PropTypes.object
};
constructor(props) {
super(props);
this.state = {
descriptionChanged: false,
newDescription: '',
newTitle: '',
done: false,
id: 0
};
this.handleDescriptionChange = this.handleDescriptionChange.bind(this);
this.handleDeleteClick = this.handleDeleteClick.bind(this);
this.changeButtons = this.changeButtons.bind(this);
this.handleSaveClick = this.handleSaveClick.bind(this);
this.handleUndoClick = this.handleUndoClick.bind(this);
this.handleTitleChange = this.handleTitleChange.bind(this);
this.handleDoneChange = this.handleDoneChange.bind(this);
}
componentWillMount() {
this.props.fetchTodo(this.props.params.id).then(() => {
this.setState({
newDescription: this.props.todo.description,
newTitle: this.props.todo.title,
done: this.props.todo.completed,
id: this.props.todo.id
});
});
}
render() {
const { todo } = this.props;
const { fields: {title, description}, handleSubmit } = this.props;
console.log("Fields: description: ", this.props.fields.description.value); //These values change as expected
console.log("Fields: title: ", this.props.fields.title.value);
if (!todo) {
return (
<h3>Loading...</h3>
);
}
return (
<div id="showTodo">
<Link id="btnBack" className="btn btn-custom" role="button" to="/todos_index"><span className="glyphicon glyphicon-arrow-left"></span></Link>
<LogoutHeader></LogoutHeader>
<div className="row">
<div className="col-md-6 col-md-offset-3">
<form onSubmit={handleSubmit(this.handleSaveClick)}>
<h3>Edit Todo</h3>
<div className={`form-group ${title.touched && title.invalid ? 'has-danger' : ''}`}>
<label>Title</label>
<input
type="text"
className="form-control"
value={this.state.newTitle}
onChange={this.handleTitleChange}
{...title} />
</div>
<div className="text-help">
{description.touched ? description.error : ''}
</div>
<div className={`form-group ${description.touched && description.invalid ? 'has-danger' : ''}`}>
<label>Description</label>
<textarea
className="form-control"
value={this.state.newDescription}
onChange={this.handleDescriptionChange}
{...description} >
</textarea>
</div>
<div className="text-help">
{description.touched ? description.error : ''}
</div>
<span className="input-group-btn">
{this.changeButtons()}
<button id="editTodoDelete" className="btn btn-custom" onClick={this.handleDeleteClick}><span className="glyphicon glyphicon-trash"></span></button>
</span>
</form>
</div>
</div>
</div>
);
}
changeButtons() { //This does not get called when there are changed in the input or textareas.
if (!this.state.descriptionChanged) {
return null;
} else {
return [
<button
type="submit"
id="editTodoSave"
className="btn btn-custom"
><span className="glyphicon glyphicon-floppy-save"></span></button>,
<button
id="editTodoRefresh"
className="btn btn-custom"
onClick={this.handleUndoClick}
><span className="glyphicon glyphicon-refresh"></span></button>
];
}
}
handleDescriptionChange(event) { //This does not get called when there is a change in the textarea
this.setState({
descriptionChanged: true,
newDescription: this.props.fields.description.value
});
}
handleTitleChange(event) { //This does not get called when there is a changed in the input field.
this.setState({
descriptionChanged: true,
newTitle: this.props.fields.title.value
});
}
handleDoneChange() {
this.setState({
done: !this.state.done
});
var props = {
completed: this.state.done
};
this.props.updateTodo(this.state.id, JSON.stringify(props));
}
handleDeleteClick() {
this.props.deleteTodo(this.state.id).then(() => {
this.context.router.push('/todos_index');
});
}
handleSaveClick(props) {
this.props.updateTodo(this.state.id, JSON.stringify(props)).then(() => {
alert("Todo updates should have been recieved in database");
this.context.router.push('/todos_index');
});
}
handleUndoClick() {
this.setState({
descriptionChanged: false,
newTitle: this.props.todo.title,
newDescription: this.props.todo.description,
errors: {
title: '',
description: ''
}
});
}
}
function validate(values) {
const errors = {};
if (!values.title) {
errors.title = 'Please enter a title';
}
if (values.title) {
if (values.title.length > 25){
errors.title = 'You exceeded 25 characters';
}
}
if (!values.description) {
errors.description = 'Please enter your description';
}
if (values.description) {
if (values.description.length > 500) {
errors.description = "You exceeded 500 characters";
}
}
return errors;
}
function mapStateToProps(state) {
return { todo: state.todos.todo };
}
export default reduxForm({
form: 'ShowTodoForm',
fields: ['title', 'description'],
validate //These configurations will be added to the application state, so reduxForm is very similar to the connect function.
//connect: first argument is mapStateToProps, second is mapDispatchToProps
//reduxForm: 1st is form configuration, 2nd is mapStateToProps, 3rd is mapDispatchToProps
}, mapStateToProps, { fetchTodo, updateTodo, deleteTodo })(ShowTodo);

Categories