Loading screen using Semantic UI React - reactjs

I'm trying to display a loading screen while the rest of the App complete loading using Semantic UI React.
The problem is that when i load the page it shows only the text "Loading" and it renders the Loader properly only after a few seconds like if it didn't have any css. The purpose of having a loader is that it has to be in front of the page while other content renders but if it has the same problem I don't know how to show content only when it rendered and ready to be displayed.
I'm using this code from the semantic ui react webpage (https://react.semantic-ui.com/elements/loader/#types-loader)
function App() {
const [loading, setLoading] = useState(true);
useEffect(() => {
setTimeout(() => {
setLoading(false);
}, 2000)
}, [])
return (
<div>
<div style={{ display: loading ? "block" : "none" }}>
<Dimmer active>
<Loader content='Loading' />
</Dimmer>
</div>
<div style={{ display: loading ? "none" : "block" }}>
// Content that needs time to render
</div>
</div>
)
}
export default App;
What can I do in order to display content only when it's done rendering?

Try putting it in a Segment. Furthermore I wouldn't use the technique you use to display or hide things, what I always do is:
{condition && <Component />}
So in this case your code would be:
{loading === true && <Segment><Dimmer active><Loader content='Loading' /></Dimmer></Segment>}
and
{loading === false && <>
{/* Content that needs time to render */}
</>}
And then of course set loading to false whenever your API calls are finished.
I hope this helps you and answers the question, if not please point it out :).

Related

How can I render one component conditionally twice and not lose internal states/unmounts?

I have one component which needs to be rendered conditionally. Renders the same component with different styles. So, I did like this
import ComponentToRender from '../../ComponentToRender'
const Main =()=> {
const [expand,setExpand] =useState(false)
return (
<div>
{!expand && <ComponentToRender {...someProps} />}
{expand && <div>
<ComponentToRender {...otherProps} />
</div>
}
<button onClick={()=>setExpand(pre => !pre)}>Expand</button>
</div>
)
}
For the above code, I get what I want in terms of UI. But, all the internal states are lost. I must render two components like that and keep the internal states. Is that possible to do that in React?
You can achieve this by keeping the component rendered unconditionally and hiding it with CSS.
You get to preserve Component‘s state for free along with the DOM state (scroll, focus, and input position). However, this solution has drawbacks, too:
You mount the component on startup, even if the user never accesses it.
You update the component even when it’s invisible.
import ComponentToRender from "../../ComponentToRender";
const Main = () => {
const [expand, setExpand] = useState(false);
return (
<div>
<div style={{ display: expand ? null : "none" }}>
<ComponentToRender {...someProps} />
</div>
<div style={{ display: !expand ? null : "none" }}>
<div>
<ComponentToRender {...otherProps} />
</div>
</div>{" "}
<button onClick={() => setExpand((pre) => !pre)}>Expand</button>
</div>
);
};
The reconciliation algorithm is such that when on next render you move from one component to component of different type (assuming they have same spot in component hierarchy), instance of old component is destroyed.
Since you have <ComponentToRender/> and another one is <div><ComponentToRender/></div>, they are different components (because one is inside a div).
Read about reconciliation.
What you can do is move the state of ComponentToRender to Main and pass it as props. Now even if the component unmounts the state will not be lost.

useEffect not running on refresh

I'm having an issue with the useEffect hook on this blog site I'm building. Im trying to fetch all the blogs from the backend so I can use them to populate this section with the latest five blogs. When I use the code below, the empty array in the useEffect prevents the infinite amount of fetch calls, which is great.
But then I run into a problem where if I refresh the page or navigate back to it I get an error on line 35 saying "cannot find mainImage of undefined".
My question is how do I have the fetchCall populate the state and do so even on a refresh so that I can still access the info I need. Thanks!
import React, {useState, useEffect} from 'react';
import CardItem from './CardItem';
import './Cards.css';
function Cards() {
const [blogs, setBlogs] = useState();
useEffect(() => {
fetchBlogs();
// eslint-disable-next-line react-hooks/exhaustive-deps
},[]);
const fetchBlogs = async () => {
console.log('ok');
const response = await fetch('http://localhost:3000/blogs');
const data = await response.json();
setBlogs(data);
};
return (
<div className='cards'>
<div className='header-container'>
<img
className='logo'
alt='logo'
src='https://Zi.imgur.com/MaLqLee.png'
/>
<h1 className='cards-title'>Hot takes and hometown bias</h1>
</div>
<div className='cards-container'>
<div className='cards-wrapper'>
<ul className='cards-items'>
<CardItem
src={blogs.length > 0 ? blogs[0].mainImage : ''}
text="Don't look now, but Zach Lavine officially kicks ass."
label='Bulls'
path='/bulls'
/>
<CardItem
src='https://i.imgur.com/EmVgHk2.jpg'
text='The curious case of Mitch Trubisky apologists.'
label='Bears'
path='/bears'
/>
</ul>
<ul className='cards-items'>
<CardItem
src='https://i.imgur.com/ZZ5dLJU.jpg'
text='How Did We Get Here? The Suddenly Bleak State of the Cubs.'
label='Cubs'
path='/cubs'
/>
<CardItem
src='https://i.imgur.com/MwgKmUM.jpg'
text='Pace and Nagy: So, how much can we blame these guys?'
label='Bears'
path='/bears'
/>
<CardItem
src='https://i.imgur.com/Y2Eorvu.jpg'
text='Thad Young: An Ode to the NBA Journeyman.'
label='Bulls'
path='/bulls'
/>
</ul>
</div>
</div>
</div>
);
}
export default Cards;
The fetch call is asynchronous. This means it is not guaranteed to be complete before the program enters the next line.
Because of this the blogs array will be empty at the first render. You can add an check in the src CardItem component to only use the value returned from the fetch call when it is available:
<CardItem
src={blogs.length > 0 ? blogs[0].mainImage : ''}
...
/>
An alternative would be to use the fact that blogs is an array and use the map operator to build one or more CardItems.
<ul className='cards-items'>
{blogs.map(blog => <CardItem
src={blog.mainImage}
...
/>)}
</ul>
I faced the same problem, and here is how I solved it..
First, I created a loading state, and set the initial state to true.
// const [singlePackage, setPackage] = useState([]);
const [isLoading, setLoading] = useState(true);
then, in the useEffect hook, I set the state to false like so..
useEffect(() => {
axios.get(baseURL).then((response) => {
setPackage(response.data);
setLoading(false);
})
}, []);
Then, I used a condition, if the loading state is true, return the spinner else return the component like so...
if (isLoading) {
return (
<div className="loadingContainer">
<Loader
type="ThreeDots"
color="#00b22d"
height={100}
width={100}
//3 secs
/>
</div>
)
} else {
return (
// your code here
)}
I am using react-loader-spinner, and just styled the container
you can install it using...
npm install react-loader-spinner --save
the style for container ...
.loadingContainer{
position: fixed;
top: 50%;
left:50%;
transform: translate(-50%,50%);
}

Keep data in inputs using ant design

I created an application with 2 steps. On each step user should add some data in inputs.
After clicking on button 2, the user changes the step. I want to create a scenario:
User fill form from first step and after that click on button 2
When user clicks on button 1, the first form should appear with the data that was added previously.
const [state, seState] = useState(1);
const one = () => {
seState(1);
};
const two = () => {
seState(2);
};
return (
<div>
{state === 1 ? <Form1 /> : <Form2 />}
<button onClick={one}>1</button>
<button onClick={two}>2</button>
</div>
);
};
Demo: https://codesandbox.io/s/basic-usage-antd480-forked-kdyyn?file=/index.js:252-615
Question: Has ant design this build in functionality? How to keep data from each step, and when user will switch the steps the data should be inside inputs?
To keep the data, no matter ant or not, you would need to make a choice. Firstly you can keep all the forms in the DOM and use CSS to hide them (sandbox):
const [state, seState] = useState(1);
const one = () => {
seState(1);
};
const two = () => {
seState(2);
};
return (
<div>
<div style={state === 1 ? { display: "block" } : { display: "none" }}>
<Form1 />
</div>
<div style={state === 2 ? { display: "block" } : { display: "none" }}>
<Form2 />
</div>
<button onClick={one}>1</button>
<button onClick={two}>2</button>
</div>
);
Secondly you can choose to re-fill the form values every time you do setState by keeping the data in a shared store such as redux or userReducer. I choose the first approach for simple forms that do not require too much logic and don't have large DOMs. Second approach can be a better choice in dynamic and complex data scenarios.

Rendering a component in order/after one has rendered (React)

I have an app that displays data from an API and a button for loading more.
The problem I have is that the button flashes on the screen before the data list is fetched and displayed.
I want the button to display only at the bottom of the page after the list.
What is a way I can do this?
The code :
useEffect(() => {
setLoading(true);
fetchTopRatedMovies(pageNumber).then((newData) =>
setApiData({
...newData,
results: [...apiData.results, ...newData.results]
})
);
setLoading(false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pageNumber]);
return (
<div className='top-rated-page-wrapper'>
{isLoading ? (
<Loader />
) : (
<>
<MovieList results={results} />
<PageButton pageLimit={pageLimit} loadMore={loadMore} />
</>
)}
</div>
To clarify.
The button loads first and flashes before the data is rendered.
The button is then in the position where I want it at the bottom of the page.
I don't want to button to be visible until the data has been rendered.
You could use conditional render to render load more button when data > 0
{ apiData ? <PageButton pageLimit={pageLimit} loadMore={loadMore} /> : null }
I believe this article will answer your question. The author uses the same hook and speaks specifically how to manage the lifecycle event with a state variable.
https://daveceddia.com/useeffect-hook-examples/

Change the direction of ag-grid to RTL

I am using ag-grid in react, and I want to change the direction to RTL, also use of local text. Based on the ag-grid documentation enableRtl={true} would change the style of the grid. It is true when we set it permanently, but when I want to change it programmatically, it does not change. I passed the variable through props to the component for changing the direction and translating the text. This is my code
const Grid= ({dir}) =>{
return (
<div
className="ag-theme-balham"
style={{ height: '100%', width: '100%' }}
>
<AgGridReact
defaultColDef={defaultColDef}
columnDefs={column}
rowData={rowData}
enableRtl={dir === 'rtl'}
localeTextFunc={(key, defaultValue) => {
return dir === 'rtl' ? gridLocal[key] : defaultValue;
}}
/>
</div>
);
}
before changing direction
after changing direction
As it should be
I searched for the solution, but I cannot find a way to solve it.
What should I do? What is wrong with my code?
Edit: where I used the Grid component
const MainLayout = (props) => {
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = useState(false);
const handleDrawerOpen = () => { setOpen(true) };
const handleDrawerClose = () => { setOpen(false) };
return (
<div >
<Topbar
setLang={props.setLang}
drawerOpen={handleDrawerOpen}
drawerClose={handleDrawerClose}
status={open}
handleSignOut={props.handleSignOut}
/>
<div style={{height:'48px'}}/>
<div className={classes.content}>
<Sidebar open={open} drawerClose={handleDrawerClose}/>
<Grid dir={theme.direction}/>
</div>
</div>
);
}
I have the same issue. After searching many times, I found only one solution.
I used angular not react, but, I can tell you the steps I used to solve this problem.
I followed the following steps:
I stored the value of the direction in the localstorage
Created a button to trigger an event that will change the value of the direction.
Get the value of the direction from the localstorage in the constructor
Inside the trigger, I changed the value on the localstorage for each click event.
finally, at each click event refresh the page with window.location.reload() to see the effect.
I know. It is not the perfect solution, but this is the only solution that worked for me. I think this problem is not found in the enterprise version of ag-grid.
Update:
I found a better solution. I use angular not React.
In angular, I used <ng-template> as follow:
<ng-template [ngIf]="AgGridDir === 'rtl'" [ngIfElse]="LeftGrid" #Withbutton>
<ag-grid-angular style="width: 100%; height: 450px;"
[class]="Table_Color_mode===Constants.light || Theme_dir===Constants.light?'ag-theme-alpine':'ag-theme-alpine-dark'"
[rowData]="ItemsMainCategories | async" [columnDefs]="columnDefs" (gridReady)="OnGridReady($event)"
[defaultColDef]="defaultColDef" [overlayLoadingTemplate]="overlayLoadingTemplate" [animateRows]="true"
[enableRtl]="true" [frameworkComponents]="agFrameworks" rowSelection="single"
(rowEditingStarted)="onRowEditingStarted($event)" (cellValueChanged)="UpdateItemMainCat($event)">
</ag-grid-angular>
</ng-template>
<ng-template #LeftGrid>
<ag-grid-angular style="width: 100%; height: 450px;"
[class]="Table_Color_mode===Constants.light || Theme_dir===Constants.light?'ag-theme-alpine':'ag-theme-alpine-dark'"
[rowData]="ItemsMainCategories | async" [columnDefs]="columnDefs" (gridReady)="OnGridReady($event)"
[defaultColDef]="defaultColDef" [overlayLoadingTemplate]="overlayLoadingTemplate" [animateRows]="true"
[enableRtl]="false" [frameworkComponents]="agFrameworks" rowSelection="single"
(rowEditingStarted)="onRowEditingStarted($event)" (cellValueChanged)="UpdateItemMainCat($event)">
</ag-grid-angular>
</ng-template>
<ng-template> is a tag created by Angular framework that can be used to render some components or HTML tags based on a condition.
As you can see, I used the same Ag-grid table with the same parameters. Each one with an ng-template. I used a variable called AgGridDir which is dynamically changed to rtl to ltr based on the user choice.
If the user chooses 'rtl' the first template is rendered and the [enableRtl]="true" is set to true, and if the user chooses 'ltr', then the second template will be rendered dynamically without reloading the page and the [enableRtl]="false".
I don't know about react much details, but, I want to share the idea.

Resources