I have a very large list of 'nested' data and I had to use 4 maps to extract all data and display it
the problem is when I click to redirect to this page it stuck for like half a second or sometimes more before even rendering the page
is there any solution on how to place a loader until this map finish extracting data
something like :
return (
{ 'map is still in progress'? <LoaderComponent/> : <ShowResult/>}
)
I tried something like the previous code and it shows the loader but it didn't even start to map
For large/expensive lists the best practice would be to use the hook useMemo
you can read more about it https://reactjs.org/docs/hooks-reference.html#usememo
implemented as
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
the second argument being an array of dependencies, e.g. page number, keyPress values, etc.
but in your case you could just use
const memoizedValue = useMemo(() => _hugeArray, []);
and then map the memoizedValue in your template
{memoizedValue.map(el, i)=> <div key={i}>{el}</div>}
Note that any function passed to useMemo runs during rendering. Restrict side effects to your useEffect hook
There's not much you can really do. In a scenario where you need to render a large list of items I'd recommend you check out react window. It was written by a developer who contributed to React and it helps when rendering large lists by only rendering the items that are in the viewport.
I would add a quick implementation to my answer but I don't know what the components of your app look like.
Related
I have a .tsx file that renders two component:
export default observer(function MyModule(props: MyModuleProps) {
....
return (
<div>
<TopPart></TopPart>
<LowerPart></LowerPart>
</div>
);
});
The problem I have is that the TopPart contains lots of sub components, so it takes much longer time to render, and the LowerPart is more important and I want to render it first, but in this code, the LowerPart won't be available until the TopPart has been rendered.
What I want to do is to first render the LowerPart, then the TopPart, without changing the layout. I am wondering how I can achieve this goal properly.
Disclaimer: this is a hack.
If the problem is server side, this is easy for react. Just throw up a placeholder while data is loading, then save it in state when loading finishes and render.
The following answer assumes this is a rendering performance problem. If it is, then you look at that rendering performance. Paginate your lists, simplify your CSS rules, profile react and see what is taking the time.
What follows may be interesting, but is probably a bad idea. React is declarative, meaning you tell the result you want and then let it crunch things to deliver that. As soon as you start telling it what order to do things in, you break that paradigm and things may get painful for you.
If you want to break up rendering you could use state to prevent the expensive component from rendering, and then use an effect to update that state after the first render, which then renders both components.
You could make a custom hook like this:
function useDeferredRender(): boolean {
const [doRender, setDoRender] = useState(false);
useEffect(() => {
if (!doRender) {
setTimeout(() => setDoRender(true), 100);
}
}, [doRender]);
return doRender;
}
This hook create the doRender state, initialized to false. Then it has an effect which sets the state to true after a brief timeout. This means that doRender will be false on the first render, and then the hook will immediately set doRender to true, which triggers a new render.
The timeout period is tricky. Too small and React may decide to batch the render, too much and you waste time. (Did I mention this was a hack?)
You would this like so:
function App() {
const renderTop = useDeferredRender();
return (
<div className="App">
{renderTop ? <TopPart /> : "..."}
<LowerPart />
</div>
);
}
Working example
One last time: this is probably a bad idea.
I'm setting the data that my flatlist component displays using a state called selectedStream. selectedStream changes every time the user presses a different group option. I've noticed that the flatlist takes 1-3 seconds to refresh all the posts that it's currently displaying already. I want there to be a loading indicator so that by the time the indicator goes away, the list is already properly displayed with the newly updated data.
<FlatList
maxToRenderPerBatch={5}
bounces={false}
windowSize={5}
ref={feedRef}
data={selectedStream}/>
Whenever we are working with anything related to the UI, sometimes we may face delays in UI re-rendering. However, we need to first figure out what is actually causing the delay.
The right question to ask about your code would be:
Is the rendering of items taking longer than expected? Or, is the data being passed with a delay because it is dependant on an API call or any other async task?
Once you answer that question, you may end up with two scenarios:
1. FlatList taking longer to render views
This doesn't usually happen as the RN FlatList will only render views that are visible to the user at any given time and will keep rendering new views as the user scrolls through the list. However, there may be some flickering issues for which you can refer to the below article:
8 Ways to optimise your RN FlatList
2. Passing the data causes the delay
This is the most common scenario, where we may call an API endpoint and get some data and then do setState to update any view/list accordingly. A general approach is to show some sort of a progress-bar that would indicate that the application is busy and thus maintaining a proper user-experience. The easiest way to do that is by conditional rendering.
A general example would be:
const [myList, setMyList] = useState();
function callAPIforMyList(){
// logic goes here
}
return {
{myList ? <ActivityIndicator .../> : <Flatlist .... />
}
The above code will check if myList is undefined or has a value. If undefined, it will render the ActivityIndicator or else the FlatList.
Another scenario could be when myList may have existing data but you need to update/replace it with new data. This way the above check may fail, so we can put another check:
const [myList, setMyList] = useState();
const [isAPIbusy, setAPIBusy] = useState(false)
function callAPIformyList() {
setAPIBusy(true)
/// other logics or async calls or redux-dispatch
setAPIBusy(false)
}
return {
{!isAPIBusy && myList ? (<Flatlist .... />) : (<ActivityIndicator .../>)
}
You can add multiple conditions using more turneries such as isAPIBusy ? <View1> : otherBoolean ? <View2> : <Default_View_When_No_Conditions_Match)/>
Hope this helps clarify your needs.
I have a very large and complex React application. It is designed to behave like a desktop application. The interface is a document style interface with tabs, each tab can be one of many different type of editor component (there are currently 14 different editor screens). It is possible to have a very large number of tabs open at once (20-30 tabs). The application was originally written all with React class components, but with newer components (and where significant refactors have been required) I've moved to functional components using hooks. I prefer the concise syntax of functions and that seems to be the recommended direction to take in general, but I've encountered a pattern from the classes that I don't know how to replicate with functions.
Basically, each screen (tab) on the app is an editor of some sort (think Microsoft office, but where you can have a spreadsheet, text document, vector image, Visio diagram, etc all in tabs within the same application... Because each screen is so distinct they manage their own internal state. I don't think Redux or anything like that is a good solution here because the amount of individually owned bits of state are so complex. Each screen needs to be able to save it's current working document to the database, and typically provides a save option. Following standard object oriented design the 'save' function is implemented as a method on the top level component for each editor. However I need to perform a 'save-all' function where I iterate through all of the open tabs and call the save method (using a reference) on each of the tabs. Something like:
openTabs.forEach((tabRef) => tabRef.current.save());
So, If I make this a functional component then I have my save method as a function assigned to a constant inside the function:
const save = () => {...}
But how can I call that from a parent? I think the save for each component should live within that component, not at a higher level. Aside from the fact that would make it very difficult to find and maintain, it also would break my modular loading which only loads the component when needed as the save would have to be at a level above the code-splitting.
The only solution to this problem that I can think of is to have a save prop on the component and a useEffect() to call the save when that save prop is changed - then I'd just need to write a dummy value of anything to that save prop to trigger a save... This seems like a very counter-intuitive and overly complex way to do it.... Or do I simply continue to stick with classes for these components?
Thankyou,
Troy
But how can I call that from a parent? I think the save for each component should live within that component, not at a higher level.
You should ask yourself if the component should be smart vs dumb (https://www.digitalocean.com/community/tutorials/react-smart-dumb-components).
Consider the following:
const Page1 = ({ onSave }) => (...);
const Page2 = ({ onSave }) => (...);
const App = () => {
const handleSavePage1 = (...) => { ... };
const handleSavePage2 = (...) => { ... };
const handleSaveAll = (...) => {
handleSavePage1();
handleSavePage2();
};
return (
<Page1 onSave={handleSavePage1} />
<Page2 onSave={handleSavePage2} />
<Button onClick={handleSaveAll}>Save all</button>
);
};
You've then separated the layout from the functionality, and can compose the application as needed.
I don't think Redux or anything like that is a good solution here because the amount of individually owned bits of state are so complex.
I don't know if for some reason Redux is totally out of the picture or not, but I think it's one of the best options in a project like this.
Where you have a separated reducer for each module, managing the module's state, also each reducer having a "saveTabX" action, all of them available to be dispatched in the Root component.
I am making a table on my react website with react-bootstrap-table-next (also known as react-bootstrap-table-2) and am trying to get an icon component to show up in the rows as data instead of what I have now, which is an x, like this:
A sample of what I currently have for data is:
const tools = [{key: 'Android Mobile', qual: "x"}]
But what I want to do is something like:
const tools = [{key: 'Android Mobile', qual: <CheckIcon/>}]
Where CheckIcon is const CheckIcon = () => <Icon icon={check} size={10}/> (where Icon is being imported from react-icons-kit).
Currently the entire page does not render when I do something like that, and I haven't been able to find much information on how to insert a component into an object with React. If there are any tips or tricks anyone has, that would be much appreciated!
Here is a stackblitz for anyone that wants to play around with the code
I think column.formatter can help you. there's a online demo: this
First, make sure that react-bootstrap-table-next props data accept React Component.
So far what I've found it does not support render a React Component inside the data object.
https://github.com/react-bootstrap-table/react-bootstrap-table2/blob/master/packages/react-bootstrap-table2/src/bootstrap-table.js#L118
Feel free if you want to submit a new feature at https://github.com/react-bootstrap-table/react-bootstrap-table2/issues/new
I ended up coming to the same conclusion as #Roman Masyhar and ended up taking the cheater's route for now until I end up just making my own table and just using the "✔" character in place of x's and just styling them for the same effect
In case anyone is curious I did also open a feature request here
i have kinda big issue in my project, working with angular 2 (changeDetection onPush) & ngrx, my project has 3 dumb components and one smart component that managing them, its looks something like this:
Sorry for the UX, im not a designer
So i have 1 reducer for the Tags and the other one for the items, the lists has the same type of item its just depends if the item is finished or not to be included of each list.
Until now sounds good, but my problem is that the Tags can filter cross over the application and the lists filter of the search box effects just on the self list and the map, on the ngOnChanges of the lists im execute the search filter and emit to the smart component the filtered items, and the smart component dispatch to store to change the "isFiltered" property.
Where's my problem is ?
I cant manage both of the filters, 'cause i added "isFiltered" property on the type of item, but its effects different for the filters, i mean i have a combineLatest that managing the filter of items looks something like that:
Rx.Observable.combineLatest(
items,
tags,
(items, tags) => {
return items.map((item) => {
item.isFiltered = item.isFiltered && tags.includes(item.tagId);
return item;
})
})
its not working well, and its kinda problem cause its a routine that the refernce to the observable changed the ngOnChange jumps and calling the dispatch again, tryed alot of things, at first i added the UPDATE_TAGS action in the items reducer and handle it there, but i read that its not the best practice to listen to the same action in 2 reducers.
After that i tryed to handle it in the same reducer but i cant know which items filtered from the tags and which from the search box.
The only way i thought about and it will work is another property on the item of isFilteredTags, and isFilteredLists.
Let me know if u have an answer for me, its will help me.
Thank you.