How to pass data from Component to form submit in ReactJS? - reactjs

I have created Dropdown which extends Component. In this I am fetching muliple dropdown values an storing it in const data. I want pass this data from Dropdown Component to another Component in the form of function.
Any help would be really great as I am new to React and facing bit challenge.
export class CascadingDropdown extends Component {
constructor(props) {
super(props)
this.state = {
...
dropdown_data: []
}
}
componentDidMount() {
axios.get('/api/fetchCategory').then(response => {
this.setState({
CategoryData: response.data
});
});
}
ChangeSubCategory = (e) => {
this.setState({
category_id: e.target.value
});
axios.get('/api/fetchSubCategory?category_id=' + e.target.value).then(response => {
// console.log(response.data);
this.setState({
SubCategoryData: response.data,
});
});
}
ChangeSubject = (e) => {
this.setState({
sub_category_id: e.target.value
});
axios.get('/api/fetchSubjects?sub_category_id=' + e.target.value).then(response => {
// console.log(response.data);
this.setState({
subject: response.data
});
});
}
storeData = (e) => {
this.setState({
subject_id: e.target.value
});
}
render() {
const dropdown_data = {
category_id: this.state.category_id,
sub_category_id: this.state.sub_category_id,
subject_id: this.state.subject_id
}
console.log(dropdown_data)
return (
<div className ="row">
. . .
</div>
)
}
}
export default CascadingDropdown
The value from dropdown_data should be passed to a function and below is the code that I have tried.
function CreateTicket() {
const [ticketInput, setTicketInput] = useState({
category_id: '',
sub_category_id: '',
subject_id: '',
other_subject: '',
file: '',
auto_message: '',
});
const handleTicketInput = (e) => {
e.persist();
setTicketInput({...ticketInput, [e.target.name]: e.target.value })
}
const submitTicket = (e) => {
e.preventDefault();
const data = {
...
}
axios.post(`/api/store-ticket`, data).then(res => {
console.log(res.data);
. . .
})
}
return (
<div className ="container">
<form onSubmit ={handleSubmit}>
<input id="emp_name" type="text" name="employee_name" className ="form-control" disabled = "disabled" onChange = {handleTicketInput} value = {empname} />
<CascadingDropdown />
<input id="form_other_subject" type="text" name="other_subject" disabled = "disabled" className ="form-control" value = {ticketInput.other_subject} onChange = {handleTicketInput} />
</form>
</div>
)
}
export default CreateTicket;

This is what i can do for nested state,
and update states, i have used single json object dropdown_data and passed down to children, its getting complex to update state of it but easy to prop down the children,
Soution 1 (OLD):
export class CascadingDropdown extends Component {
constructor(props) {
super(props)
this.state = {
dropdown_data: {
subject_id, category_id, sub_category_id
},
CategoryData,
SubCategoryData,
subject
}
}
this.ChangeSubCategory = this.ChangeSubCategory.bind(this);
this.ChangeSubject = this.ChangeSubject.bind(this);
this.storeData = this.storeData.bind(this);
componentDidMount() {
axios.get('/api/fetchCategory').then(response => {
this.setState({
...this.state,
CategoryData: response.data
});
});
}
ChangeSubCategory = (e) => {
this.setState({
...this.state,
dropdown_data: {
...this.state.dropdown_data,
category_id: e.target.value
}
});
axios.get('/api/fetchSubCategory?category_id=' + e.target.value).then(response => {
// console.log(response.data);
this.setState({
...this.state,
SubCategoryData: response.data,
});
});
}
ChangeSubject = (e) => {
this.setState({
...this.state,
dropdown_data: {
...this.state.dropdown_data,
sub_category_id: e.target.value
}
});
axios.get('/api/fetchSubjects?sub_category_id=' + e.target.value).then(response => {
// console.log(response.data);
this.setState({
...this.state,
subject: response.data
});
});
}
storeData = (e) => {
this.setState({
...this.state,
dropdown_data: {
...this.state.dropdown_data,
subject_id: e.target.value
}
});
}
render() {
console.log(this.state.dropdown_data)
return (
<div className ="row">
<CreateTicket dropdown_data={this.state.dropdown_data}/>
</div>
)
}
}
export default CascadingDropdown
you can even pass function callbacks too,
<CreateTicket dropdown_data={this.state.dropdown_data} ChangeSubCategory={ChangeSubCategory} ChangeSubject={ChangeSubject} storeData={storeData}/
this is how you can get your parent state down into functional componenet
function CreateTicket(props) {
const [dropdown_data, setDropdown_data] = useState(props.dropdown_data); // use hooks now
//const dropdown_data = this.props.dropdown_data;
}
my new edited answer as per your new question is as follows,
Soution 2 (NEW):
export class CascadingDropdown extends Component {
constructor(props) {
super(props)
this.state = {
dropdown_data: {
subject_id:this.props.subject_id, category_id:this.props.category_id, sub_category_id:this.props.sub_category_id
},
CategoryData,
SubCategoryData,
subject
}
}
// this.ChangeSubCategory = this.ChangeSubCategory.bind(this);
// this.ChangeSubject = this.ChangeSubject.bind(this);
// this.storeData = this.storeData.bind(this);
componentDidMount() {
axios.get('/api/fetchCategory').then(response => {
this.setState({
...this.state,
CategoryData: response.data
});
});
}
ChangeSubCategory = (e) => {
this.props.ChangeSubCategory(e.target.value)
this.setState({
...this.state,
dropdown_data: {
...this.state.dropdown_data,
category_id: e.target.value
}
});
axios.get('/api/fetchSubCategory?category_id=' + e.target.value).then(response => {
// console.log(response.data);
this.setState({
...this.state,
SubCategoryData: response.data,
});
});
}
ChangeSubject = (e) => {
this.props.ChangeSubject(e.target.value);
this.setState({
...this.state,
dropdown_data: {
...this.state.dropdown_data,
sub_category_id: e.target.value
}
});
axios.get('/api/fetchSubjects?sub_category_id=' + e.target.value).then(response => {
// console.log(response.data);
this.setState({
...this.state,
subject: response.data
});
});
}
storeData = (e) => {
this.props.storeData(e.target.value);
this.setState({
...this.state,
dropdown_data: {
...this.state.dropdown_data,
subject_id: e.target.value
}
});
}
render() {
console.log(this.state.dropdown_data)
return (
<div className ="row">
<CreateTicket />
</div>
)
}
}
export default CascadingDropdown
and its parent function is,
function CreateTicket(props) {
const [dropdown_data, setDropdown_data] = useState({
subject_id:"", category_id:"", sub_category_id:""
}); // use hooks now
ChangeSubCategory=(category_id)=>{
setDropdown_data({...dropdown_data,category_id})
}
ChangeSubject=(sub_category_id)=>{
setDropdown_data({...dropdown_data,sub_category_id})
}
storeData=(subject_id)=>{
setDropdown_data({...dropdown_data,subject_id})
}
return (
<div className ="container">
<form onSubmit ={handleSubmit}>
<input id="emp_name" type="text" name="employee_name" className ="form-control" disabled = "disabled" onChange = {handleTicketInput} value = {empname} />
<CascadingDropdown dropdown_data={dropdown_data} ChangeSubCategory={ChangeSubCategory} ChangeSubject={ChangeSubject} storeData={storeData}/>
<input id="form_other_subject" type="text" name="other_subject" disabled = "disabled" className ="form-control" value = {ticketInput.other_subject} onChange = {handleTicketInput} />
</form>
</div>
)
}
i hope this will work now,

Related

Warning: Can't perform a React state update on an unmounted component....componentWillUnmount method

I'm very new to React and React Native and I am getting this warning when I switch screens. Also, the console.log keeps repeating infinitely, how do I fix it?
class DecodeScreen extends Component {
state = {
data: this.props.navigation.getParam("data", "NO-QR"),
bookData: '',
bookFound: false
}
bookSearch = () => {
query = `https://librarydb-19b20.firebaseio.com/books/${this.state.data}.json`,
axios.get(query)
.then((response) => {
const data = response.data ? response.data : false
console.log(data)
if (data) {
this.setState({
bookData: data,
bookFound: true
})
}
}).catch((error) => {
this.setState({
bookFound: false
})
})
}
renderContent = () => {
if (this.state.bookFound) {
return(
<View>
<TextH5>{this.state.bookData.title}</TextH5>
<TextH5>{this.state.bookData.author}</TextH5>
<TextH5>{this.state.bookData.publisher}</TextH5>
<TextH5>{this.state.bookData.isbn}</TextH5>
</View>
)
}
else {
return <TextH5>beer not found</TextH5>
}
}
componentDidMount() {
this.bookSearch()
}
render() {
{this.bookSearch()}
return (
<Container>
<TextH5>{this.state.data}</TextH5>
{this.renderContent()}
</Container>
);
}}
export default DecodeScreen;
the console.log outputthe warning
You can try this approach to see if it fixes the problem.
isMounted = false;
class DecodeScreen extends Component {
state = {
data: this.props.navigation.getParam("data", "NO-QR"),
bookData: "",
bookFound: false,
};
bookSearch = () => {
this.isMounted = true;
(query = `https://librarydb-19b20.firebaseio.com/books/${this.state.data}.json`),
axios
.get(query)
.then((response) => {
const data = response.data ? response.data : false;
console.log(data);
if (data) {
if (this.isMounted) {
this.setState({
bookData: data,
bookFound: true,
});
}
}
})
.catch((error) => {
this.setState({
bookFound: false,
});
});
};
renderContent = () => {
if (this.state.bookFound) {
return (
<View>
<TextH5>{this.state.bookData.title}</TextH5>
<TextH5>{this.state.bookData.author}</TextH5>
<TextH5>{this.state.bookData.publisher}</TextH5>
<TextH5>{this.state.bookData.isbn}</TextH5>
</View>
);
} else {
return <TextH5>beer not found</TextH5>;
}
};
componentDidMount() {
this.isMounted = true;
this.bookSearch();
}
componentWillUnmount() {
this.isMounted = false;
}
render() {
{
this.bookSearch();
}
return (
<Container>
<TextH5>{this.state.data}</TextH5>
{this.renderContent()}
</Container>
);
}
}
export default DecodeScreen;
You have to use componentDidMount method to do api call
componentDidMount() {
this.bookSearch()
}
Read about react life cycle method

How to disable stripe pay button when payment is processing using reactjs?

I have created using reactjs stripe card section and in card section, adding card element and in the checkout form render it and in checkout form pay button is also there but I want to disable the pay button unless response gets from backend?
Here is my code:-
class CheckoutForm extends React.Component {
handleCallback = status => {
if (status === "success") {
message.success("Payment is successfull");
this.props.history.push("/main");
} else {
message.error("Some error occoured");
}
};
handleSubmit = ev => {
ev.preventDefault();
const { userDetails, user, tempPassDate } = this.props;
const { paymentId } = this.props;
this.props.stripe
.createPaymentMethod("card", { billing_details: { name: "Jenny Rosen" } })
.then(({ paymentMethod }) => {
console.log("Received Stripe PaymentMethod:", paymentMethod.id);
this.props.payment(
{
paymentMethodId: paymentMethod.id,
paymentId: paymentId,
},
);
})
.catch(err => console.log(err));
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<CardSection/>
<button>
Pay
</button>
</form>
);
}
}
Manage that in state like:
class CheckoutForm extends React.Component {
constructor(props){
super(props);
this.state = {
disableBtn: false;
}
}
handleCallback = status => {
if (status === "success") {
message.success("Payment is successfull");
this.props.history.push("/main");
} else {
message.error("Some error occoured");
}
};
handleSubmit = ev => {
ev.preventDefault();
this.setState({ disableBtn: true });
const { userDetails, user, tempPassDate } = this.props;
const { paymentId } = this.props;
this.props.stripe
.createPaymentMethod("card", { billing_details: { name: "Jenny Rosen" } })
.then(({ paymentMethod }) => {
console.log("Received Stripe PaymentMethod:", paymentMethod.id);
this.props.payment(
{
paymentMethodId: paymentMethod.id,
paymentId: paymentId,
},
);
this.setState({ disableBtn: false });
})
.catch(err => console.log(err));
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<CardSection/>
<button disabled={this.state.disableBtn}>
Pay
</button>
</form>
);
}
}

Problems in passing parameters to custom middleware

Hi I am passing an id to the custom middleware I created in redux. here is my Dispatch function:
/** NPM Packages */
import React, { Component } from "react";
import { connect } from "react-redux";
import fetchCategory from "../../../actions/categoryAction";
import deleteCategory from "../../../actions/categoryDeleteAction";
/** Custom Packages */
import List from "../List";
//import API from "../../utils/api";
class Category extends Component {
constructor(props) {
super(props);
this.state = { categories: [], mesg: "", mesgType: "" };
this.onChange = this.onChange.bind(this);
}
/** Hook Call */
componentDidMount = async () => {
if (this.props.location.state)
this.setState({
mesg: this.props.location.state.mesg,
mesgType: this.props.location.state.mesgType
});
this.closeMesg();
this.props.fetchCategory();
};
/** Methods */
onChange = e => this.setState({ [e.target.name]: e.target.value });
onDelete = id => {
/*await API.delete("categories/" + id)
.then(res => {
const categories = this.state.categories.filter(el => el._id !== id);
this.setState({
categories: categories,
mesg: res.data.msg,
mesgType: "success"
});
})
.catch(err => console.log(err));
this.closeMesg();*/
this.props.deleteCategory(id);
};
closeMesg = () =>
setTimeout(
function() {
this.setState({ mesg: "", mesgType: "" });
}.bind(this),
30000
);
/** Rendering the Template */
render() {
const { mesg, mesgType } = this.state;
return (
<>
{mesg ? (
<div
className={"alert alert-" + mesgType + " text-white mb-3"}
role="alert"
>
{mesg}
</div>
) : (
""
)}
<List
listData={this.props.categories}
listName="category"
_handleDelete={this.onDelete.bind(this)}
/>
</>
);
}
}
const matchStatestoProps = state => {
return { categories: state.categories };
};
const dispatchStatestoProps = dispatch => {
return {
fetchCategory: () => dispatch(fetchCategory),
deleteCategory: (id) =>dispatch(deleteCategory(id))
};
};
export default connect(matchStatestoProps,dispatchStatestoProps)(Category);
Here is action:
import API from "../pages/utils/api";
const deleteCategory = (id,dispatch) =>{
API.delete("categories/" + id)
.then(() => {
dispatch({type:'DELETE'})
})
.catch(err => console.log(err));
}
export default deleteCategory;
here is my reducer:
const initState = [];
const categoryReducer = (state = initState, { type, payload }) => {
switch (type) {
case "FETCH_CATEGORY":
return payload.data;
case "DELETE":
alert("Data Deleted");
default:
return state;
}
};
export default categoryReducer;
Now i am getting this error :
Error: Actions must be plain objects. Use custom middleware for async actions.
▶ 6 stack frames were collapsed.
deleteCategory
src/app/pages/isolated/category/Categories.js:92
89 | const dispatchStatestoProps = dispatch => {
90 | return {
91 | fetchCategory: () => dispatch(fetchCategory),
> 92 | deleteCategory: (id) =>dispatch(deleteCategory(id))
93 | };
94 | };
95 |
If possible could someone say where am I going wrong?
Or is there any other way to pass parameters to the custom middleware?
Use redux-thunk as a middleware to handle async operations in Actions. Actions must be return plain objects, which means, something like below,
{
type : "DELETE",
payload : data
}
But in the Delete action you return a Promise, so that's why it is saying Actions must be return only plain objects. To handle this situation you can use a middleware like reduxt-thunk.
https://www.npmjs.com/package/redux-thunk
Edit -
If you use the Redux-thunk, In the catch bloack of the deleteCategory, you didn't return any object. You just console.log() the error. return the err from catch block and see if you see any errors with your API call
You can refer my react project
class Category extends Component {
constructor(props) {
super(props);
this.state = { categories: [], mesg: "", mesgType: "" };
this.onChange = this.onChange.bind(this);
}
/** Hook Call */
componentDidMount = async () => {
if (this.props.location.state)
this.setState({
mesg: this.props.location.state.mesg,
mesgType: this.props.location.state.mesgType
});
this.closeMesg();
this.props.dispatch(fetchCategory()); // we have to dispatch our action like
};
/** Methods */
onChange = e => this.setState({ [e.target.name]: e.target.value });
onDelete = id => {
this.props.dispatch(deleteCategory(id));
};
closeMesg = () =>
setTimeout(
function() {
this.setState({ mesg: "", mesgType: "" });
}.bind(this),
30000
);
/** Rendering the Template */
render() {
const { mesg, mesgType } = this.state;
return (
<>
{mesg ? (
<div
className={"alert alert-" + mesgType + " text-white mb-3"}
role="alert"
>
{mesg}
</div>
) : (
""
)}
<List
listData={this.props.categories}
listName="category"
_handleDelete={this.onDelete.bind(this)}
/>
</>
);
}
}
const matchStatestoProps = state => {
return { categories: state.categories };
};
export default connect(matchStatestoProps)(Category);
In your deleteCategory : you can access dispatch like
const deleteCategory = (id) => async (dispatch) =>{
await API.delete("categories/" + id)
.then(() => {
dispatch({type:'DELETE'})
})
.catch(err => console.log(err));
}
export default deleteCategory;

how to update value in reactjs

constructor(props) {
super(props)
this.state = {
isEdit: false,
currentProduct : {
sku: '',
productName: '',
description: '',
duration: '',
},
}
}
handleChange = (e) => {
this.setState({
currentProduct: {
...this.state.currentProduct,
[e.target.name]: e.target.value
}
})
}
clickHandle = (e) => {
e.preventDefault()
const currentProduct = {...this.state.currentProduct}
currentProduct.id = this.props.match.params.id
this.props.updateProduct(currentProduct)
this.props.history.push('/')
}
When updating field it updates the values but when i goes again to update single value it update only that and removes the other don't know why
handleChange = (e) => {
this.setState({
...this.state.currentProduct,
[e.target.name]: e.target.value
})
}
you are not destructuring entire state first. so do ...state. otherwise isEdit field will be lost.
handleChange = e => {
this.setState({
...this.state,
currentProduct: {
...this.state.currentProduct,
[e.target.name]: e.target.value
}
});
};

how to clear the input value after setting the state in react?

I am making a todo app in react and after taking input from the user on submit i am making a post request to update in the database and then updating the state. and then i am trying to clear the input field using
e.target.value = "". But this is not working. Iam fairly new to JS and React. can some one point me what i am doing wrong here.
class TodoApp extends Component {
constructor(props) {
super(props);
this.state = {
todos: [],
};`enter code here
this.handleTodos = this.handleTodos.bind(this);
this.handleLogout = this.handleLogout.bind(this);
this.removeTodo = this.removeTodo.bind(this);
};
componentDidMount() {
const authStr = 'Bearer ' + getJWTToken();
axios.get('/tasks', {
'headers': {
'Authorization': authStr
}
}).then(res => {
// console.log(res.data);
this.setState({
todos: res.data,
})
}).catch(err => {
console.log(err);
});
};
removeTodo = id => {
// console.log(id)
const authStr = 'Bearer ' + getJWTToken();
axios.delete('/tasks/' + id, {
'headers': {
'Authorization': authStr
}
}).then(res => {
// console.log(res.data);
let newTodos = [...this.state.todos];
newTodos = newTodos.filter(todo => {
return todo._id !== id;
});
//Update the State
this.setState({
todos: newTodos
});
}).catch(err => {
console.log(err);
});
};
handleTodos = e => {
e.preventDefault();
const authStr = 'Bearer ' + getJWTToken();
var todo = {
description: e.target.value
}
console.log(todo)
axios.post('/tasks', todo, {
'headers': {
'Authorization': authStr
}
}).then(res => {
// console.log(res.data);
this.setState({
todos: this.state.todos.concat(res.data)
})
}).catch(err => {
console.log(err)
});
e.target.value = "";
// console.log(todo);
};
handleLogout() {
localStorage.removeItem('jwtToken');
this.props.history.push("/");
}
render() {
const listLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
};
return (
<div className="container-fluid App">
<div className="todoContainer">
<Header
handleLogout={this.handleLogout}
/>
<h1 style={{ paddingTop: "10px" }}>TODO App</h1>
<Input
placeholder="What needs to be done?"
onPressEnter={this.handleTodos}
/>
<List
itemLayout="horizontal"
locale={{ emptyText: "No Todos" }}
dataSource={this.state.todos}
renderItem={item => (
<TodoItem
todo={item}
removeTodo={this.removeTodo}
/>
)}
/>
</div>
</div>
);
};
};
export default TodoApp;
The value of your input field should be bound to your state to properly control it. You can modify your state declaration like this:
this.state = {
todos: [],
whatToDo: ""
}
and bind your input field to your state like this:
<Input
placeholder="What needs to be done?"
onPressEnter={this.handleTodos}
value={this.state.whatToDo}
onChange={this.onInputChange} // will update the state on each change
/>
then create the onInputChange function:
onInputChange= (event, data) => {
this.setState({ whatToDo: data.value });
}
and lastly, change the line
e.target.value = "";
to
this.setState({ whatToDo: "" });
try this,
handleTodos = e => {
e.preventDefault();
const authStr = 'Bearer ' + getJWTToken();
var todo = {
description: e.target.value
}
console.log(todo)
axios.post('/tasks', todo, {
'headers': {
'Authorization': authStr
}
}).then(res => {
// console.log(res.data);
this.setState({
todos: this.state.todos.concat(res.data)
})
}).catch(err => {
console.log(err)
});
//RESET FIELD
e.target.reset()
};
You can use Refs to clear input text. This is the working solution.Also, follow this Reactjs link for more information.
class App extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
this.state = {
inputField: ""
};
}
keyHandler = event => {
if (event.key === "Enter") {
console.log(this.inputRef.current.value);
this.setState({ inputField: event.target.value });
this.inputRef.current.value = "";
}
};
render() {
return (
<div>
<input
type="text"
onKeyPress={this.keyHandler}
ref={this.inputRef}
/>
<p>{this.state.inputField}</p>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<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>
Allow me to tell you that this is not the react way of working generally speaking with inputs. I could also try to fix your error, but I prefer to bring you on the right path. Have a look at this doc page: https://reactjs.org/docs/forms.html
Particularly this stackoverflow answer: What are controlled components and uncontrolled components in ReactJS?
Using controlled components, to clear the input field, you just call setState emptying the content of a particular state variable. Please, have a look at the example I'm about to write:
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
}
handleChange = (event) => {
this.setState({value: event.target.value});
}
handleSubmit = (event) => {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
emptyInput = () => {
alert('Emptying input');
this.setState({ value: '' });
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
<button onClick={emptyInput}>Empty input</button>
</form>
);
}
}

Resources