Probably a newb-ish question. I'm new to react.
Following some blog posts and such, I was able to build a page that higher order components and componentDidMount
to load data from an API and render it to the page. It works great and the code looks clean, I can't figure out though, how to pass some kind of onClick through the higher order component, ultimately I'd like to move the guts of the fetch into a function that can be called by both componentDidMount
as well as <Button onClick={}>Reload</Button>
. Halp pls
import React, { Component } from 'react';
import {Button, CardColumns, Card, CardHeader, CardBody} from 'reactstrap';
const API = 'http://localhost:3000/';
const DEFAULT_QUERY = 'endpoint';
const withFetching = (url) => (Comp) =>
class WithFetching extends Component {
constructor(props) {
super(props);
this.state = {
data: {},
isLoading: false,
error: null,
};
// Goes here?
this.onClick = () => {
console.log("Handled!");
};
}
componentDidMount() {
this.setState({ isLoading: true });
fetch(url)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong ...');
}
})
.then(data => this.setState({ data, isLoading: false }))
.catch(error => this.setState({ error, isLoading: false }));
}
// Or here maybe??
this.onClick = () => {
console.log("Handled!");
};
render() {
// How do I pass it in?
return <Comp { ...this.props } { ...this.state } onClick={this.onClick} />
}
}
// How do I tell this component to expect it to recieve the handler?
const App = ({ data, isLoading, error }) => {
const hits = data.hits || [];
console.log(data);
if (error) {
return <p>{error.message}</p>;
}
if (isLoading) {
return <p>Loading ...</p>;
}
return (
<div className="animated fadeIn">
<CardColumns className="cols-2">
<Card>
<CardHeader>
API Card!
<div className="card-actions">
</div>
</CardHeader>
<CardBody>
{hits.map(hit =>
<div key={hit.foo}>
<h3>{hit.foo}</h3>
_____
</div>
)}
<Button onClick={props.onClick}>Launch demo modal</Button>
</CardBody>
</Card>
</CardColumns>
</div>
);
}
export default withFetching(API + DEFAULT_QUERY)(App);
Here is the blog post that led me to the architecture I'm using:
Edit: I can create a function outside the class so that it is available everywhere, but the reason I originally wanted to leave it in was so that I could actually alter the state and re-render the card with the new data. Trying to figure out the proper use of bind()
to make that work... JS makes me feel so dumb sometimes :p
答案 0 :(得分:0)
Have you considered making the function a root level function outside of all the classes? Then any component can call it.
For example:
import React, { Component } from 'react';
import {Button, CardColumns, Card, CardHeader, CardBody} from 'reactstrap';
const API = 'http://localhost:3000/';
const DEFAULT_QUERY = 'endpoint';
function sharedUtilityFunction(){
// Do something here
}
const withFetching = (url) => (Comp) =>
class WithFetching extends Component {
constructor(props) {
super(props);
this.state = {
data: {},
isLoading: false,
error: null,
};
}
componentDidMount() {
sharedUtilityFunction();
this.setState({ isLoading: true });
fetch(url)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong ...');
}
})
.then(data => this.setState({ data, isLoading: false }))
.catch(error => this.setState({ error, isLoading: false }));
}
render() {
return <Comp { ...this.props } { ...this.state } />
}
}
// How do I tell this component to expect it to recieve the handler?
const App = ({ data, isLoading, error }) => {
const hits = data.hits || [];
console.log(data);
if (error) {
return <p>{error.message}</p>;
}
if (isLoading) {
return <p>Loading ...</p>;
}
return (
<div className="animated fadeIn">
<CardColumns className="cols-2">
<Card>
<CardHeader>
API Card!
<div className="card-actions">
</div>
</CardHeader>
<CardBody>
{hits.map(hit =>
<div key={hit.foo}>
<h3>{hit.foo}</h3>
_____
</div>
)}
<Button onClick={() => sharedUtilityFunction()}>Launch demo modal</Button>
</CardBody>
</Card>
</CardColumns>
</div>
);
}
export default withFetching(API + DEFAULT_QUERY)(App);
答案 1 :(得分:0)
我正在寻找的答案确实是将函数作为prop传递给低阶组件。为了做到这一点,我需要改变竞争对手预期的方式:const App = props => {
TBD是否有其他的重复,但我认为状态已经被传递给了道具。 ..正确调用函数会导致isLoading
渲染,这是一个好兆头。
import React, { Component } from 'react';
import {Button, CardColumns, Card, CardHeader, CardBody} from 'reactstrap';
const API = 'http://localhost:3000/';
const DEFAULT_QUERY = 'endpoint';
const withFetching = (url) => (Comp) =>
class WithFetching extends Component {
constructor(props) {
super(props);
this.state = {
data: {},
isLoading: false,
error: null,
};
this.goFetch = this.goFetch.bind(this);
}
goFetch() {
this.setState({ isLoading: true });
fetch(url)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong ...');
}
})
.then(data => this.setState({ data, isLoading: false }))
.catch(error => this.setState({ error, isLoading: false }));
}
componentDidMount() {
this.goFetch();
}
render() {
return <Comp { ...this.props } { ...this.state } goFetch={this.goFetch}/>
}
}
const App = props => {
const hits = props.data.hits || [];
if (props.error) {
return <p>{props.error.message}</p>;
}
if (props.isLoading) {
return <p>Loading ...</p>;
}
return (
<div className="animated fadeIn">
<CardColumns className="cols-2">
<Card>
<CardHeader>
API Card!
<div className="card-actions">
</div>
</CardHeader>
<CardBody>
{hits.map((hit, index) =>
<div key={index}>
Foo: <h3>{hit.foo}</h3>
</div>
)}
<Button onClick={props.goFetch}>Refresh</Button>
</CardBody>
</Card>
</CardColumns>
</div>
);
}
export default withFetching(API + DEFAULT_QUERY)(App);