I have a React.Component inside a ReactModal.
class Course extends React.Component {
constructor(props) {
super(props)
this.state = {
isModalOpen: false,
}
}
handleModalOpen = event => {
this.setState({ isModalOpen: true })
}
handleModalClose = event => {
this.setState({ isModalOpen: false })
}
render() {
<ReactModal
isOpen={this.state.isModalOpen}
onRequestClose={this.handleModalClose}
contentLabel="Purchase a Course"
style={customStyles}>
<CheckoutComponent handleClose={this.handleModalClose}/>
</ReactModal>
class CheckoutForm extends React.Component {
constructor(props) {
super(props);
}
handleSubmit = (ev) => {
axios.post(`${process.env.API_URL}purchase`, charge)
.then(function (response) {
this.props.handleClose();
}
}
I would like to close the react modal upon successful post of the http request.
However, this is undefined.
How can I do it?
The problem is in here
axios.post(`${process.env.API_URL}purchase`, charge)
// here
.then(function (response) {
this.props.handleClose();
})
You need to use arrow functions. With normal functions, this will the the annonimous function's this. Using arrow functions solve this problem and you get the component's this.
axios.post(`${process.env.API_URL}purchase`, charge)
// arrow function
.then(response => {
this.props.handleClose();
})
This answer explain it well.
Related
I have an api which i want to filter the data and place the filterd into a state
export default class ModifyPage_Single extends React.Component {
constructor(props) {
super(props)
this.state = {data:[],idd:""}
}
componentWillMount() {
fetch("removed api")
.then(response => response.json())
.then((data) =>{
this.setState({data:data})
})
}
render() {
const test = this.state.data.map((e) => { if(e.ID === this.props.location.productdetailProps.productdetail) {this.setState({idd:e.PP})} })
But i keep getting this error Unhandled Rejection (Error): Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
How can i solve so that the fitered out api goes into the state?
Thanks in advance
you should update in componentMount, not in render():
export default class ModifyPage_Single extends React.Component {
constructor(props) {
super(props);
this.state = { data: [], idd: "" };
}
componentWillMount() {
fetch("removed api")
.then((response) => response.json())
.then((data) => {
this.setState({ data: data });
data.forEach((e) => {
if (e.ID === this.props.location.productdetailProps.productdetail) {
this.setState({ idd: e.PP });
}
});
});
}
render() {
return null;
}
}
You can update the state in lifecycle methods, updating it in render is anti pattern
componentDidMount() {
fetch("removed api")
.then(response => response.json())
.then((data) =>{
this.setState({data:data})
const iddObj = data.find((el) => el.ID === this.props.location.productdetailProps.productdetail)
if(iddObj ){
this.setState({idd:iddObj.PP})
}
})
}
So, I need to get the response from a request and then send it to another component. The problem is that my request isn't finished when the component call happens. So what I end up getting on the "TableComponent" is an empty array
This is the component I'm making the request at:
class Carrinho extends Component {
constructor(props) {
super(props);
this.getMateriais()
}
async getMateriais() {
let service = new MateriaisService();
service.getMateriais().then(res => res.json()).then((result) => {
this.setState({materiais: result})
})
}
render() {
return (
<div>
<TableComponent materiais={this.state.materiais} itens={this.state.array_teste}></TableComponent>
</div>
);
}
And this is how I'm setting my state on TableComponent.js :
constructor(props) {
super(props);
this.state = {
materiais : props.materiais,
}
This won't work, because this.getMateriais() call in the constructor, won't trigger a new render. You'll need to use componentDidMount life cycle and async/await syntax.
class Carrinho extends Component {
constructor(props) {
super(props);
this.getMateriais()
}
async componentDidMount(){
await this.getMateriais();
}
async getMateriais() {
let service = new MateriaisService();
const result = await service.getMateriais();
const data = await result.json();
this.setState({ materiais: result });
}
render() {
return (
<div>
<TableComponent materiais={this.state.materiais} itens={this.state.array_teste}></TableComponent>
</div>
);
}
However, async/await is not recommendable to deal with promises in React programming model. Instead, you should render a different component or a loading, while waiting.
class Carrinho extends Component {
constructor(props) {
super(props);
}
componentDidMount(){
this.getMateriais();
}
getMateriais() {
let service = new MateriaisService();
service.getMateriais().then(res => res.json()).then((result) => {
this.setState({materiais: result})
})
}
render() {
return (
<div>
{this.state.materiais && <TableComponent materiais={this.state.materiais} itens={this.state.array_teste}></TableComponent>}
</div>
);
}
I've seen examples that show how to pass props from a child to its parent with a onClick on onChange event on the child component, but am struggling to figure out how to pass props up passively.
What i'd like is to have the child component that performs a fetch operation and then passes the response up to the parent.
// PARENT component
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
homeLink: 'inital'
}
}
handleNamechange(newName) {
this.setState({
homeLink: newName
})
}
render() {
return(
<section>
<h1>{this.state.homeLink}</h1>
<GetUserComponent
changeLink={this.handleNamechange.bind(this)}
/>
</section>
)
}
}
export default App;
And the part I struggle with is sending the props up to the parent WITHOUT the onClick, and just pass the props once the fetch is complete
// CHILD Component
class GetUserComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
homeLink: 'xxxx'
}
}
componentDidMount() {
fetch('https://someapi/getusername', {
})
.then(function(response) {
return response.json()
})
.then((data) => {
this.setState(
{ username: data }
)
})
}
onChangeLink() {
this.props.changeLink(this.state.homeLink)
}
render() {
return (
<div>
<span onClick={this.onChangeLink.bind(this)}
>Change Header Link</span>
</div>
)
}
}
export default GetUserComponent;
I'm not sure if I'm doing this wrong, or whether this simply can't be done and you HAVE to use the click event, but either way would really appreciate your help.
You have to call the parents function:
componentDidMount() {
fetch('https://someapi/getusername', {
})
.then(function(response) {
return response.json()
})
.then((data) => {
this.props.changeLink(data);
})
}
It will then execute the handleNamechange function and update the parents state.
In your case, I think the parent must do the fetch, and give as props the result to the children.
If you really need the child fetch the data, you have to call the callback changeLink given as a props from the parent to the child as it :
componentDidMount() {
fetch('https://someapi/getusername', {
})
.then(function(response) {
return response.json()
})
.then((data) => {
this.setState(
{ username: data }
)
this.props.changeLink(data)
})
}
I have a component, which has to download a JSON file and then iterate over it and display each element from the JSON on the screen.
I'm kinda new with React, used to be ng dev. In Angular, I used to do it with lifecycle hooks, e.g. ngOnInit/ngAfterViewInit (get some JSON file and then lunch the iteration func). How can I achieve it in React? Is it possible to reach it with lifecycle hooks, like ComponentWillMount or ComponentDidMount.
My code (it's surely wrong):
export default class ExampleClass extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
}
}
componentWillMount(){
getData();
}
render() {
return (
<ul>
{this.state.data.map((v, i) => <li key={i}>{v}</li>)}
</ul>
)
};
}
const getData = () => {
axios.get(//someURL//)
.then(function (response) {
this.setState({data: response.data});
})
.catch(function (error) {
console.log(error);
})
};
How to force React to get the JSON before rendering the component?
Thank you so much.
Making an AJAX request in ComponentWillMount works. https://facebook.github.io/react/docs/react-component.html#componentwillmount
You could also just work that logic into your constructor depending on your exact needs.
https://facebook.github.io/react/docs/react-component.html#constructor
export default class ExampleClass extends React.Component {
constructor(props) {
super(props)
this.state = {
data: [],
}
axios.get(/*someURL*/)
.then(function (response) {
this.setState({data: response.data});
})
.catch(function (error) {
console.log(error);
})
}
}
You can do a simple if statement in your render function.
render () {
if (Boolean(this.state.data.length)) {
return <ul>{this.state.data.map((v, i) => <li key={i}>{v}</li>)}</ul>
}
return null
}
You can also use a higher order component to do the same thing.
const renderIfData = WrappedComponent => class RenderIfData extends Component {
state = {
data: []
}
componentWillMount() {
fetchData()
}
render() {
if (Boolean(this.state.data.length)) {
return <WrappedComponent {...this.state} />
}
return null
}
}
Then you can wrap the presentational layer with the HOC.
renderIfData(ExampleClass)
Not sure what version of React you are using but you may need to use <noscript> instead of null.
This is essentially preventing your component from rendering until it has all the data.
I have problem with automatically re-rendering view, when state is changed.
State has been changed, but render() is not called. But when I call this.forceUpdate(), everything is ok, but I think that's not the best solution.
Can someone help me with that ?
class TODOItems extends React.Component {
constructor() {
super();
this.loadItems();
}
loadItems() {
this.state = {
todos: Store.getItems()
};
}
componentDidMount(){
//this loads new items to this.state.todos, but render() is not called
Store.addChangeListener(() => { this.loadItems(); this.forceUpdate(); });
}
componentWillUnmount(){
Store.removeChangeListener(() => { this.loadItems(); });
}
render() {
console.log("data changed, re-render");
//...
}}
You should be using this.state = {}; (like in your loadItems() method) from the constructor when you are declaring the initial state. When you want to update the items, use this.setState({}). For example:
constructor() {
super();
this.state = {
todos: Store.getItems()
};
}
reloadItems() {
this.setState({
todos: Store.getItems()
});
}
and update your componentDidMount:
Store.addChangeListener(() => { this.reloadItems(); });
You sholdn't mutate this.state directly. You should use this.setState method.
Change loadItems:
loadItems() {
this.setState({
todos: Store.getItems()
});
}
More in react docs
In your component, whenever you directly manipulate state you need to use the following:
this.setState({});
Complete code:
class TODOItems extends React.Component {
constructor() {
super();
this.loadItems();
}
loadItems() {
let newState = Store.getItems();
this.setState = {
todos: newState
};
}
componentDidMount(){
//this loads new items to this.state.todos, but render() is not called
Store.addChangeListener(() => { this.loadItems(); this.forceUpdate(); });
}
componentWillUnmount(){
Store.removeChangeListener(() => { this.loadItems(); });
}
render() {
console.log("data changed, re-render");
//...
}}