I have a component which fetch data from backend. Here I have already added loading while the component fetch on data from backend. But the problem is if there is no data available there it still keeps on showing that loading. Instead I want to show something like There is nothing In here Text on screen if loading is complete and no data is available there. I want to know how to implement this any help would be great.
This is my code
class FootStep extends React.Component {
componentDidMount() {
this.props.getFootstep();
}
render() {
const { loaded, loading, error, data } = this.props.footstep.data;
console.log("footstepContainer props", this.props);
if (loaded) {
return <Footstep data={data} />;
} else return <p>loading</p>;
}
}
Loaded should be true if response is successful and
Check if the the length of data is zero then show message
if(loaded && data.length === 0){
return <p>No Data available!</p>
}
else if (loaded) {
return <Footstep data={data} />;
}
else
return <p>loading</p>;
Can we do something like this?
if (loaded) {
return <Footstep data={data} />;
} else if(loaded && data.length === 0){
return <p>No Data available!</p>
} else return <p>loading</p>;
Related
Using react-query in new project and having small issue.
Requirement is to show loading spinner on empty page before data is loaded and than after user fetches new results with different query to show spinner above previous results.
First case is pretty easy and well documented:
const { isLoading, error, data } = useQuery(["list", query], () =>
fetch(`https://api/list/${query}`)
);
if (isLoading) return <p>Loading...</p>;
return <div>{data.map((item) => <ListItem key={item.id} item={item}/></div>
But, cant figure out second case - because, after query changes react-query doing fetch of new results and data from useQuery is empty as result I get default empty array and in that case falls to that condition - if (isLoading && !data.length )
const { isLoading, error, data=[] } = useQuery(["list", query], () =>
fetch(`https://api/list/${query}`)
);
if (isLoading && !data.length ) return <p>Loading...</p>;
return <div>
{data.map((item) => <ListItem key={item.id} item={item}/>
{isLoading && <Spinner/>}
</div>
When you have no cache (first query fetch or after 5 min garbage collector), isLoading switch from true to false (and status === "loading").
But when you already have data in cache and re-fetch (or use query in an other component), useQuery should return previous cached data and re-fetch in background.
In that case, isLoading is always false but you have the props "isFetching" that switch from true to false.
In your example, if the variable "query" passed on the array is different between calls, it's normal to have no result. The cache key is build with all variables on the array.
const query = "something"
const { isLoading, error, data } = useQuery(["list",query], () =>
fetch(`https://api/list/${query}`)
);
const query = "somethingElse"
const { isLoading, error, data } = useQuery(["list",query], () =>
fetch(`https://api/list/${query}`)
);
In that case, cache is not shared because "query" is different on every useQuery
You can use is isFetching for every time new data is fetched
const { isFetching, isError, isSuccess, data } = useQuery('key', fnc())
if (isFetching) {
return <LoaderSpinner />
}
else if (isError) {
return "error encountered"
}
return (
{ isSuccess && data?.map((item) => <li>{item?.name}</li>)}
)
The problem with this code is that it is not checking if the data array is empty before attempting to map over it. If the data array is empty, this code will throw an error. To fix this, the code should check if the data array is empty before attempting to map over it.
const { isFetching, isError, isSuccess, data } = useQuery('key', fnc());
if (isFetching) {
return <LoaderSpinner />
} else if (isError) {
return "error encountered"
} else if (isSuccess && data?.length) {
return data?.map((item) => <li>{item?.name}</li>)
}
<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>
I am trying to get No list items only when there's nothing coming from the backend. Right now, onload, I get the loading spinner and No List items before I fetch the data.
So, I thought I would add a timeout to deal with this so that it will only show up after the fetching is done, and there are no items
getList() {
if(this.state.list.length != 0){
return (this.state.list.map(data => {
return <div data={data} key={data.id}/>
}))
}else{
return <div>No List items</div>
}
}
render() {
return (
<div>
<Spinner active={this.state.active} />
<div>{setTimeout(this.getList, 1000)}</div>
</div>
);
}
}
When i use this, I am getting numbers on the browser. The active state of spinner changes on componentDidMount to false
That's what setTimeout returns: an id number, which you can use later if you want to cancel the timeout.
The render method is synchronous. If you want to render nothing for the case where you don't have data, then you can have render return null. Then in componentDidMount, do any async work you need, and when it completes, call this.setState to update the state and rerender (this time without a null)
class Items extends React.Component {
constructor(props) {
super();
this.state = {
active: true,
is_loading: false,
}
}
componentDidMount() {
this.timeout_number = setTimeout(() => {
this.setState({
active: false,
is_loading: true
});
}, 1000);
}
componentWillUnmount() {
clearTimeout(this.timeout_number);
}
getList() {
if(this.state.list.length)
return this.state.list.map(data => <div data={data} key={data.id}/>)
else
return <div>No List items</div>
}
render() {
return (
<div>
<Spinner active={this.state.active} />
{this.state.is_loading
? this.getList()
: null}
</div>
);
}
}
export default Items;
Don't use a timeout here. I would just set the initial state of list to null. Then just flip your logic so that it is:
getList() {
if(this.state.list && this.state.list.length == 0){
return <div> No List items </div>
}else{
return (this.state.list.map(data => {
return <div data={data} key={data.id}/>
}))
}
}
There are 100 ways to solve this but this is the easiest based on your code. ALso don't forget the difference between != and !==.
I am new to reactjs and I'm creating a project using it. I have a problem while using multiple components. Here is the scenario
when the page is loaded both component is loaded values of type,getcpeyearlysval and getvnfyearlysval are set
setDataMeter1(){
//here i want only type cpe componnet will load
}
<Speedometer type = "cpe" getcpeyearlysval={this.objSpeedometer.getcpeyearlys}/>
<Speedometer type = "vnf" getvnfyearlysval={this.objSpeedometer.getvnfyearlys}/>
Now I want when I to call this.setDataMeter1 function only type cpe component should work, but the problem is both calls.
<select value={this.state.selectValue} onChange={this.setDataMeter1}>
In Speedometer component:
renderIcon(key, val) {
if(key == 'vnf') {
console.log("vnf")
}
if(key == 'cpe'){
console.log("cpe")// this condition should met
}
}
render() {
return (
this.renderIcon(this.props.type, this.props)
);
}
}
But my problem is both conditions mets in speedometer components.
renderIcon(props) {
if(props.type == 'vnf') {
return <Text>hey this is vnf </Text>
}
if(props.type == 'cpe'){
return <Text>hey this is cpe </Text>
}
}
render() {
return (
this.renderIcon(this.props)
);
}
So my react class calls to a json response and then renders.
But as it stands, the class will render on initial load then render again when response has come back.
To get around this I have do this -
componentDidMount: function() {
axios.get.......
.then(res => {
const jobs = res.data;
this.setState({ jobs });
});
},
render: function () {
if (Object.keys(this.state.jobs).length == 0)
{
}
else {
return (
<div>
{this.state.jobs.data.map(function (ob) {
return <li key={ob.id}>{ob.name}</li>
})}
</div>
)
}
return null;
}
});
Is there a nicer way to do this? without using an if statement?
React always has to render in the initial load. So you just need to render null like you already did. Code wise, maybe you can write
return this.state.jobs.data ? <YourTemplate /> : null;
which is cleaner.
in many of my components I am fetching API data and therefor I need to wait until that data was loaded. Otherwise I am getting errors because some methods are, of course, not available.
My api query looks like this
componentDidMount() {
prismicApi(prismicEndpoint).then((api) =>
api.form('everything')
.ref(api.master())
.query(Prismic.Predicates.at("my.page.uid", this.props.params.uid))
.submit((err, res) => {
if (res.results.length > 0) {
this.setState({doc: res.results[0]});
} else {
this.setState({notFound: true});
}
}))
}
For that I've created this structure that I have been using in all of these documents:
render() {
if (this.state.notFound) {
return (<Error404 />);
} else if (this.state.doc == null || !this.state.doc) {
return (<Loading />);
} else {
return (
<div className="page">
{this.state.doc.getSliceZone('page.body').slices.map(function(slice, i){
return (<SliceZone slice={slice} key={i} />)
})}
</div>
)
}
}
I wanted to move this into a component called Document that looks like this here:
export default class Document extends React.Component {
static defaultProps = {
doc: null,
notFound: false
}
static propTypes = {
doc: React.PropTypes.oneOfType([
React.PropTypes.object,
React.PropTypes.array
]),
notFound: React.PropTypes.bool.isRequired
}
render() {
if (this.props.notFound) {
return (<Error404 />);
} else if (this.props.doc == null || !this.props.doc) {
return (<Loading />);
} else {
return (
<div className="page">
{this.props.children}
</div>
)
}
}
}
and then I tried to use it like this here:
<Document doc={this.state.doc} notFound={this.state.notFound}>
{this.state.doc.getSliceZone('page.body').slices.map(function(slice, i){
return (<SliceZone slice={slice} key={i} />)
})}
</Document>
Though on the second example the error messages are showing up quickly (until the data is loaded) and then disappear. What am I doing wrong? Why is the first example working and the second doesnt?
try this
<Document doc={this.state.doc} notFound={this.state.notFound}>
{ this.state.doc && this.state.doc.getSliceZone('page.body').slices.map(function(slice, i){
return (<SliceZone slice={slice} key={i} />)
})}
</Document>
in your variant, you see an error becuase this.state.doc is null, untill data is loaded, and you see null reference exception, looks like.
In 1st case, it does not calculate, in 2nd case it calculates first and then sent as a parameter "children" to your Document control