React pass ajax data to children - javascript

How I can pass to child component from parent data requested by ajax call?
For example I have code like that
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
async componentDidMount() {
const response = await fetch();
this.setState(response.data);
}
render() {
return (
<ChildComponent data={this.state} /> // data={}
);
}
}
Problem here is ChildComponent will be mount before data will be fetch so I will get empty object data in ChildComponent.

Check if data is available or not
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
async componentDidMount() {
const response = await fetch();
this.setState(response.data);
}
render() { // Return if response is Object
return Object.keys(this.state).length > 0
? <ChildComponent data={this.state} /> // data={}
: <div>Loading...</div>
}
render() { // Return if response is Array
return this.state.length > 0
? <ChildComponent data={this.state} /> // data={}
: <div>Loading...</div>
}
}

You can decide to render the children only if there is some data.
For this, maybe don't replace the whole state with data but create a separate key. It will be easier if you need to add some other state after.
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
async componentDidMount() {
const response = await fetch();
this.setState({ data: response.data });
}
render() {
const { data } = this.state;
return data ? <ChildComponent data={data} /> : null;
}
}

Related

How do i handle HOC from it's wrapped component?

I have a HOC to handling loading using axios,
here's a code of withAxiosHOC:
export default (url, WrapComponent) => {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
isLoading:false,
isFailed:false,
isError:false,
message:null,
};
}
componentDidMount(){
this.callAPI()
}
async callAPI(){
//show loading
// handle API and pass to wrapped component
// hide loading
}
render() {
if (this.state.isLoading) {
return (
//show loading UI
)
} else if(this.state.isFailed){
return(
//show Failed UI
)
} else if(this.state.isError){
return(
//show Error UI
)
}
return (
<WrapComponent data={this.state.data} {...this.props} />
)
}
}
}
and usually i'm used HOC like this, let say Home.js:
export default withAxiosHttp(
'https://reactnative.dev/movies.json',
class Home extends React.Component{
constructor(props) {
super(props);
this.state = {
data:props.data
}
}
render() {
return(
<View style={{flex:1, backgroundColor:Color.black}}>
<MyText>{JSON.stringify(this.state.data, null, 2)}</MyText>
</View>
)
}
}
)
but sometimes i need to call the URL depend on state of my wrapped component,
something like this Suggestion.js:
export default withAxiosHttp(
'https://exampleAPIneedDynamicValue.com/suggestion?lat='+this.state.position.lat+'&long='+this.state.position.long,
class Suggestion extends React.Component{
constructor(props) {
super(props);
this.state = {
data:props.data,
position:{lat:null, long:null}
}
}
componentDidMount(){
let tempPosition = this.state.position
tempPosition.lat = MyLatitude
tempPosition.long = MyLongitude
this.setState({position:tempPosition})
}
render() {
return(
<View style={{flex:1, backgroundColor:Color.black}}>
<MyText>{JSON.stringify(this.state.data, null, 2)}</MyText>
</View>
)
}
}
)
as you see in Suggestion.js, i need to call a URL depending on lat and long of position state,
and lat long state only available in wrappedComponent of HOC,
My Question:
How do i handle HOC to run when lat long state is available in wrappedComponent?
does my HOC can be used to POST method also?
Please give me a suggestion/answer in React Native scope
You can modify the url parameter is a function instead of a string.
In withAxiosHOC
async callAPI(){
axios.get(url(this.state)).then(data => {
//logic here
})
}
Your Suggestion.js will be
export default withAxiosHttp(
(state) => {
return 'https://exampleAPIneedDynamicValue.com/suggestion?lat='+state.position.lat+'&long='+state.position.long
},
class Suggestion extends React.Component{
constructor(props) {
super(props);
this.state = {
data:props.data,
position:{lat:null, long:null}
}
}
componentDidMount(){
let tempPosition = this.state.position
tempPosition.lat = MyLatitude
tempPosition.long = MyLongitude
this.setState({position:tempPosition})
}
render() {
return(
<View style={{flex:1, backgroundColor:Color.black}}>
<MyText>{JSON.stringify(this.state.data, null, 2)}</MyText>
</View>
)
}
}
)

How to call child's method from parent without using Refs?

Let's say I've a parent component A and a child B:
A:
class A {
constructor() {
this.state = {data: []};
}
handleClick = () => {
// api call
// set data state to the returned value from api
// call B's createTable method
}
render() {
return(
<div>
<button onClick={()=> this.handleClick()}>Fetch data</button>
<B data={this.state.data} />
</div>
}
}
B:
class B {
constructor() {
this.state = {...};
}
createTable = () => {
const { data } = this.props;
// do smth
}
render() {
return(...);
}
}
I want to call createTable method from A without using Refs.
What I've done so far is using componentDidUpdate life cycle method in B to check if data prop has changed or not, If it changed call createTable method but I want to know is this right? or there's a better way of doing it because I feel it is kinda hacky or maybe bad design.
class B {
constructor() {
this.state = {...};
}
componentDidUpdate(prevProps) {
const { data } = this.props;
if (data !== prevProps.data) {
this.createTable();
}
}
createTable = () => {
const { data } = this.props;
// do smth
}
render() {
return(...);
}
}
NOTE I don't want to use hooks either just class based component.
The following example might be useful
class Parent extends Component {
render() {
return (
<div>
<Child setClick={click => this.clickChild = click}/>
<button onClick={() => this.clickChild()}>Click</button>
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.getAlert = this.getAlert.bind(this);
}
componentDidMount() {
this.props.setClick(this.getAlert);
}
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}

Reactjs Passing props down 2 components

In my project I have a payment form which conditionally renders 2 Stripe elements (PaymentRequestForm.js & CheckoutForm.js), I have passed the Props down from the main form component FullfillRequest.js to PaymentRequestForm.js, however I'm struggling with passing the same props down to CheckoutForm.js.
The function of the props is to enable submit of the form upon Stripe payment is completion.
With the current set-up, the error is this.props.setComplete() is undefined in CheckoutForm.js.
FulfillRequest.js
setComplete = val => {
this.setState({
complete: val
});
};
render() {
return <PaymentRequestForm
setComplete={this.setComplete}
complete={this.state.complete}
requestId={this.props.requestId}
/>
<Button disabled={loading || !this.state.complete} >
Fulfill Request
</Button>
}
PaymentRequestForm.js
class PaymentRequestForm extends React.Component {
constructor(props) {
super(props);
paymentRequest.on("token", async ev => {
const response = await fetch();
if (response.ok) {
this.props.setComplete(true);
}
});
this.state = {
canMakePayment: false,
paymentRequest,
complete: false
};
}
render() {
if (this.props.complete) return <h1>Purchase Complete</h1>;
return this.state.canMakePayment ? (
<PaymentRequestButtonElement />
) : (
<CheckoutForm
setComplete={this.setComplete}
complete={this.state.complete}
requestId={this.props.requestId}
/>
);
CheckoutForm.js
class CheckoutForm extends Component {
constructor(props) {
super(props);
this.state = { complete: false };
this.submit = this.submit.bind(this);
}
async submit(ev) {
let response = await fetch();
if (response.ok) {
this.props.setComplete(true);
}
}
render() {
if (this.props.complete) return <h1>Purchase Complete</h1>;
return (
<Button onClick={this.submit}>
Send
</Button>
);
}
}
Any help will be greatly appreciated, thanks!
Inside PaymentRequstForm setComplete is accessible via this.props.setComplete not this.setComplete (cause it's coming from FulfillRequest)
render() {
if (this.props.complete) return <h1>Purchase Complete</h1>;
return this.state.canMakePayment ? (
<PaymentRequestButtonElement />
) : (
<CheckoutForm
setComplete={this.props.setComplete}
complete={this.state.complete}
requestId={this.props.requestId}
/>
);

React - How to render components inside a function outside of render plus execute same function outside of render?

I have a function outside of render. That function returns (conditionally) a component, that function is beeing triggered not inside render, but inside componentWillReceiveProps (which was necessary due to other facts).
My problem is that the function does not end up returning the component and I dont know why. When I call that function inside render, then of it works, but I cant do that as I must call it inside componentWillReceiveProps. Any ideas? Thanks!!
class App extends React.Component {
componentWillReceiveProps(nextProps) {
if (nextProps.user != this.props.user) {
this.getData(nextProps.user)
}
}
getData() {
if (...) {
return <Child />
}
}
render() {
return (
<div>{this.getData}</div>
);
}
}
const Child = () => {
return <h1>Hello</h1>
}
Create a state called data in the constructor as follows:
constructor(props){
super(props);
this.state={data:""};
}
Now, {this.getdata} inside render() with {this.state.data}
Also replace componentWillReceiveProps as follows:
componentWillReceiveProps(nextProps) {
if (nextProps.user != this.props.user) {
var newdata = this.getData(nextProps.user)
this.setState({data:newdata});
}
}
Because you can't return children from other hooks than render you will need to keep them in a state:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
someChildren: null
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.user != this.props.user) {
this.setState({ someChildren: this.getData(nextProps.user) });
}
}
getData() {
if (...) {
return <Child />;
}
return null
}
render() {
return <div>{this.state.someChildren}</div>;
}
}
When you component will receive new props, it will re-render automatically, doing like following you should have you component to re-render and being updated:
class App extends React.Component {
getData: () => {
if (...) {
return <Child />
}
return null;
};
render() {
return (
<div>{this.getData()}</div>
);
}
}
componentWillReceiveProps is React lifecycle method which is invoked as soon as your React Component receive a prop by the parent. Actions that could be performed in there are for example update the state what you are doing instead is calling a getDate method which is returning a React Component .
A possible implementation could be:
class App extends React.Component {
getData() {
const { user } = this.props;
return user ? <Child /> : <div />
}
render() {
return (
<div>
{this.getData()}
</div>
)
}
}
const Child = () => {
return <h1>Hello</h1>
}
You can only return JSX data in render and not in the other lifecycle function to render. Also render method is pure so for the same input it returns the same output and hence react is able to rightly optimise on performance for the same by maintaining a virtual dom, so you would just write
class App extends React.Component {
getData() {
if (...) {
return <Child />
}
}
render() {
return (
<div>
{this.getData()}
</div>
)
}
}
const Child = () => {
return <h1>Hello</h1>
}
and it would have a the same effect, also if you further optimise by using React.PureComponent, so that render is called on when there is a prop change. React.PureComponent implements shouldComponentUpdate with a shallow prop and state comparison.
class App extends React.PureComponent {
componentWillReceiveProps(nextProps) {
if (nextProps.user != this.props.user) {
this.getData(nextProps.user)
}
}
getData() {
if (...) {
return <Child />
}
}
render() {
return (
<div>
{this.getData()}
</div>
)
}
}
However to do what you want, you would actually store the date in state of component and then render the data based on state in render method
class App extends React.Component {
constructor(props) {
super(props);
this.state {
data: this.getData(props)
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.user != this.props.user) {
this.getData(nextProps.user)
}
}
getData(props) {
if (...) {
const newData;
// update newData based on receivedProps here
// store the data in state
this.setState({data: newData});
}
return [];
}
render() {
return (
<div>
{this.state.data.map((obj) => return <Child data={obj}/>)}
</div>
)
}
}

React component method this.state.myState undefined after returning from child

I am having a Parent component (BookApplication) and a child component (SearchBox) in React. The SearchBox has an input field, and should give the input back to the parent for handling the event. That is working fine, but when i am back in the parent component in the method handleSearch the this.state... is undefined.
TypeError: Cannot read property 'books' of undefined
But searchInput has the value it should have.
But i need the books from this.state.books again :/
I understand that in the method handleSearch i am working in it's scope, so this.... is the context of handleSearch... but how do i get the arguments of it's component the BookApplication again?
I am still learning javascript, and i thought this shouldnt be a problem, because a function can always use the variables of it's parent object?
class BookApplication extends React.Component {
constructor() {
super();
this.state = {books: []};
}
componentDidMount() {
$.get(PATH, function (result) {
this.setState({
books: result
});
}.bind(this));
}
handleSearch(searchInput) {
//Sort the books list
var sortedList = [];
this.state.books.map(
function (currentBook) {
currentBook.keys().forEach(
function (key, pos) {
if (key.contains(searchInput)) {
sortedList.push(currentBook)
}
}
)
}
);
}
render() {
return (
<div>
<SearchBox onSearch={this.handleSearch}/>
<div className="book-list">
{this.state.books.map(function (currentBook) {
return <Book book={currentBook} key={currentBook.id}/>;
}) }
</div>
</div>
);
}
Here also my SearchBox:
class SearchBox extends React.Component {
constructor(props) {
super(props);
this.state = {searchFieldInput: ''};
this.handleSearchChange = this.handleSearchChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSearchChange(event) {
this.setState({searchFieldInput: event.target.value});
}
handleSubmit(e) {
//Prevent the browser's defeault action of submitting the form
e.preventDefault();
var searchFieldInput = this.state.searchFieldInput.trim();
//Call the passed callback function
this.props.onSearch({searchFieldInput: searchFieldInput});
}
render() {
return (
<div className="book-search">
<input
type="text"
value={this.state.searchFieldInput}
onChange={this.handleSearchChange}
placeholder="Search..."
className="search-bar"
/>
<button onClick={this.handleSubmit} className="search-button">Search</button>
</div>
);
}
}
If your question is how to get parent's context from child component, then try
class ParentComponent extends React.Component {
...
...
clickHandler(event){}
render(){
<ChildComponent parent={this}/>
}
}
class ChildComponent extends React.Component {
constructor(props){
super(props);
}
render(){
let parent = this.props.parent;
return <button onClick={parent.clickHandler}></button>
}
}
And you will get an error here
componentDidMount() {
$.get(PATH, function (result) {
this.setState({
books: result
});
}.bind(this));
}
Because this in the callback function not referred to your Component's context. You should keep component's context in variable
componentDidMount() {
let self = this;
$.get(PATH, function (result) {
self.setState({
books: result
});
}.bind(this));
}
Finally decision is
constructor(props) {
super(props);
this.state = {books: []};
//add the following line into your code
this.handleSearch = this.handleSearch.bind(this);
}

Categories

Resources