import React, { Component } from "react";
import Sidebar from "../../components/Sidebar";
import API from "../../utils/API";
import PostContainer from "../../components/PostContainer"
import { withRouter } from 'react-router'
import "./index.css";
class Posts extends Component {
constructor(props) {
super(props);
this.state = {
posts: [],
carMake: "",
carModel: "",
carYear: 0
};
}
componentDidMount() {
const { carMake, carModel, carYear } = this.props.match.params;
if (carMake && carModel && carYear) {
console.log("component mounted, calling loadPostsMakeModelYear");
console.log(carMake, carModel, carYear);
this.loadPostsMakeModelYear(carMake, carModel, carYear);
} else if (carMake && carModel) {
console.log("component mounted, calling loadPostsMakeModel");
console.log(carMake, carModel, carYear);
this.loadPostsMakeModel(carMake, carModel);
} else if (carMake) {
console.log("component mounted, calling loadPostsMake");
console.log(carMake, carModel, carYear);
this.loadPostsMake(carMake);
} else {
console.log("component mounted, calling loadPosts");
console.log(carMake, carModel, carYear);
this.loadPosts();
}
}
componentDidUpdate() {
//???
}
loadPosts = () => {
API.getAllPosts({})
.then(resp => {
this.setState({
posts: resp.data
});
})
.catch(function (error) {
console.log(error);
});
}
loadPostsMake = (carMake) => {
API.getPostByMake(carMake)
.then(resp => {
console.log("loadPostsMake success");
this.setState({
posts: resp.data,
carMake: ""
});
})
.catch(function (error) {
console.log(error);
});
}
loadPostsMakeModel = (carMake, carModel) => {
API.getPostByMakeModel(carMake, carModel)
.then(resp => {
console.log("loadPostsMakeModel success");
this.setState({
posts: resp.data,
carMake: "",
carModel: ""
})
})
.catch(function (error) {
console.log(error);
});
}
loadPostsMakeModelYear = (carMake, carModel, carYear) => {
API.getPostByMakeModelYear(carMake, carModel, carYear)
.then(resp => {
console.log("loadPostsMakeModelYear success");
this.setState({
posts: resp.data,
carMake: "",
carModel: "",
carYear: 0
})
})
.catch(function (error) {
console.log(error);
});
}
handleInputChange = event => {
const { name, value } = event.target;
console.log("input Changed " + name + " " + value)
this.setState({
[name]: value
});
};
handleFormSubmit = event => {
event.preventDefault();
console.log("Handling form submit");
const { carMake, carModel, carYear } = this.state;
const { history } = this.props
if (carMake && carModel && carYear) {
history.push('/search/' + carMake + '/' + carModel + '/' + carYear)
} else if (carMake && carModel) {
history.push('/search/' + carMake + '/' + carModel)
} else if (carMake) {
history.push('/search/' + carMake)
} else {
history.push('/')
}
}
render() {
return (
<div>
<div className="container-fluid">
<div className="row">
<div className="col-2">
<Sidebar
handleInputChange={this.handleInputChange}
handleFormSubmit={this.handleFormSubmit}
/>
</div>
<div className="col-8 offset-1">
{this.state.posts.map(post => (
<PostContainer post={post} />
))}
</div>
</div>
</div>
</div>
);
}
}
export default withRouter(Posts);
I am trying to create a way for users to filter through posts based on 3 different parameters. Depending on how many of the 3 they decide to use, a different API call will be made accordingly. The current code works to bring up all posts on initial render, and the search function works only one time.
If the user attempts to use the search function without returning home, the state and URL update, but the components do not update.
I read that I may be able to use something like componentDidUpdate(), but I don't understand how I can get the previous props, or even the previous state.
componentDidUpdate(prevProps, prevState, snapshot) where prevProps is the props of your component before update, same with the prevState, this.state and this.props are fields of your current component. You can compare them to know you fetched something (prevProps.field !== this.props.field && this.props.field === something good), for example
Related
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,
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
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>
);
}
}
I currently have a context provider.
componentDidMount() {
if (this.state.memberID === null) {
try {
this.checkAuthUser();
} catch (e) {
console.error(e);
}
}
}
checkAuthUser = () => {
new Promise((resolve, reject) => {
this.props.firebase.auth.onAuthStateChanged(authUser => {
if(authUser) {
resolve(authUser);
} else {
reject(new Error("Not authorized"));
}
})
})
.then( authDetails => {
this.props.firebase.getOrgID(authDetails.uid).on('value', snapshot => {
const setSnapshot = snapshot.val();
const getOrganizationID = Object.keys(setSnapshot)[0];
this.setState({ memberID: authDetails.uid, orgID: getOrganizationID })
})
})
.catch(err => console.log(err))
}
When I try to use this in another component:
static contextType = AuthDetailsContext;
componentDidMount() {
console.log('here is context: ' + this.context.orgID);
if(this.context.orgID) {
this.setState({currentOrganization: this.context.orgID, loading: true}, () => {
this.getMembersInDB('1');
})
}
}
My console.log is null. Means the context isn't registering yet. Any idea what I'm doing wrong?
Your design here seems flawed i.e. when your provider is mounted you send the API request and then when your descendant component is mounted you try to use it - these operations will happen in quick succession, far quicker than it would take for an API call to return from a server.
In your provider, if you must have a user before the component mounts then you need to delay rendering the child components until your API response completes i.e.
const AuthDetailsContext = React.createContext(null);
class AuthDetailsProvider extends PureComponent {
...
componentDidMount() {
const { firebase } = this.props;
firebase.auth.onAuthStateChanged(authUser => {
if (!authUser) {
// Maybe set some other state state to inform the user?
this.setState({ authError: new Error('Not Authorised') });
return;
}
firebase.getOrgID(authUser.uid)
.on('value', snapshot => {
const setSnapshot = snapshot.val();
const getOrganizationID = Object.keys(setSnapshot)[0];
this.setState({
authError: null,
memberID: authUsermemberID.uid,
orgID: getOrganizationID
});
});
})
}
render() {
if (this.state.authError) return <b style={{ color: red }}>{this.state.error.message}</b>;
if (!this.state.memberID) return <b>Authenticating...</b>
return (
<AuthDetailsContext.Provider value={this.state}>
{this.props.children}
</AuthDetailsContext.Provider>
);
}
}
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;