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.
Related
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.
I'm using this documentation https://github.com/atlassian/react-beautiful-dnd to add drag and drop functionality to my app.
Working tutorial version: https://codepen.io/c15mnw/pen/wXyZrW?editors=0010
My rendition- https://stackblitz.com/edit/react-ef9gan
I've used most of the source code, but I've run into a stopping point since my app doesn't use fixed data like the example, I'm having trouble getting the list items to move into place.
My app uses conditional rendering to show append fields onto the current view based on whatever button click so there is conditional rendering in what should be in the drag and drop items.
I am at the point that I can drag individual input fields (clicking and holding the green area) but I can't drop them into place/change order.
Looking at my code, I believe that the problem is either in how I am mapping the data in return statement or in the way state was set in my application.
Tutorial code (omitted in my version):
const getItems = (count: number): Item[] =>
Array.from({ length: count }, (v, k) => k).map(k => ({
id: `item-${k}`,
content: `item ${k}`
}));
which is then passed into state...
this.state = {
items: getItems(6),
};
instead my state is passed like this in components/InputShow.jsx
this.state = {
items: [this.props.node]
};
this.onDragEnd = this.onDragEnd.bind(this);
}
I've taken a look at a few S/O answers including Conditionally render list with Higher Order Component & Conditional List in ReactJS Based On State among others but I didn't think the pertained to my situation.
I'm still pretty new to React, so I'm not sure exactly where I'm going wrong. Any help would be greatly appreciated
I am working on a simple React.JS frontend piece.
I essentially have a browsing SPA for historical data. The design has a bunch of filters that I need to populate one at a time, starting the top one in my logic hierarchy.
I do something like:
componentWillMount() {
if (!this.props.advertisers) {
this.props.loadAdvertisers();
}
}
Once advertisers array has been loaded, I map it to options of a select component (using react-select), set the selection to the first item in the list and load the next list - campaigns.
As far as I understand, the best way to do this is still componentWillReceiveProps() and I am a little perplexed how this should be done differently, given that componentWillReceiveProps is being phased out.
My code looks like:
componentWillReceiveProps(nextProps) {
if (nextProps.advertisers && !this.props.advertisers) {
const advertiser = nextProps.advertisers[0];
this.setState({
advertiser: {
value: advertiser['id'],
label: advertiser['name']
}
});
nextProps.loadCampaigns(advertiser['id']);
}
// if campaigns list changed, reload the next filtered array
if (nextProps.campaigns && this.props.campaigns !== nextProps.campaigns) {
...
}
This worked fine, until I decided to add a loading indicator. I mapped state's loading property e.g. for campaigns it gets exposed via this.props.campaignsLoading then do:
return (this.props.campaignsLoading || this.props...Loading || ...) ?
<ProgressIndicator> : <MainContentPanel>
The problem is now, my state does not get set correctly inside componentWillReceiveProps().
The project is using #rematch and I initially tried this with #rematch/loading plugin and when the problem happened, thought the plugin does it wrong, somehow. Then, I mapped loading properties manually, and just added two more dispatches to manually set the loading flag.
All the props are being set/unset correctly, but my state is not being set and nothing works. Any suggestions?
When you do
if (nextProps.advertisers && !this.props.advertisers) {
You are not comparing the next and the previous props. "this.props.advertisers" is probably already set so you never go into the setState line. Although using componentWillReceiveProps is no longer the recommended way to go (You Probably Don't Need Derived State), what you probably want to do roughly is:
if (nextProps.advertisers && nextProps.advertisers !== !this.props.advertisers) {
I'm building what is essentially a Trello clone using React and Redux. Thanks to #Sagiv b.g, I've got logic in place to allow users to create multiple <Board/>, <List/> and <Card/> instances using a single Redux store.
But that logic breaks down when I want to move <Card/> components between different <List/>.
Example
If I create two lists, one called "To Do" and one called "Done", my Redux logic matches the separate React <List/> component instances to the correct <Board/> instance using my boards() reducer like so:
case ADD_LIST:
return state.map(board => {
if (board.id !== action.boardId) return board;
return {
...board,
lists: lists(board.lists, action)
};
});
If I then create a card in my "To Do" list called "Finish Kanban project", the logic is basically the same in Redux for matching the correct <Card/> instance to the correct <List/> instance, except it happens in the lists() reducer:
case ADD_CARD:
return state.map(list => {
if (list.listId !== action.listId) return list;
return {
...list,
cards: cards(list.cards, action)
};
});
So far so good! But if I want to move the card I just created from the "To Do" list to the "Done" list, I can't quite figure out the logic with React Beautiful DND and Redux combined.
React Beautiful DND wants to reorder the entire array, which means I need to update my array of cards in Redux accordingly. So I'm stuck on how to take a card out of one list and move it to another list using my current Redux logic.
Thanks for any direction. I'm ready to abandon Redux or React Beautiful DND altogether, but wanted to see if anyone has any ideas on how to make it work.
I'm currently working through the same issue. If you watch the free egghead videos for React Beautiful DnD: https://egghead.io/courses/beautiful-and-accessible-drag-and-drop-with-react-beautiful-dnd you will see that regardless of whether you are using redux, you need to write logic in your onDragEnd handler to update the state. In the video, he is using the useState() hook to manage the state of his lists. In your case, you would emulate what he does but do it in redux by dispatching an action. You can use the same state shape and basically the same code as what is shown in the video. You just need to do it in redux.
My idea is to mantain the list of filtered users (suggestions) as state on the component, when the input changes, the state is updated.
How can I display the filtered list below the text box?
One option is 'datalist' tag (HTML5), but the list is already filtered, and part of the functionality of this tag is filtering.
I can't use any library or framework.
English is not my native language, sorry if you find some mistake.
Thanks.
Try a component from a design library, like the Material-UI autocomplete component http://www.material-ui.com/#/components/auto-complete
The dataSource attribute represents the array of autocomplete options.
How I did it was to pass in the dataList array as a prop and filterByField prop so that you can change what to filter, then add an event listener to the input (onChange) that passes the value to a function that filters the dataList.
onChangeInput(e) {
const { dataList, filterByField } = this.props;
const filteredDataList = dataList.filter(items => items[filterByField].toLowerCase().startsWith(e.target.value.toLowerCase()) );
// update internal component state to trigger render of dropdown list
this.setState({filteredList: filteredDataList});
}
I also added a check for no matches found so I can show a message:
if (filteredDataList.length === 0) {
this.setState({noMatchFound: true});
}
Then in my render() I simply check if filteredList isn't null and show an unordered list that I use css to display below the input.
{this.state.filteredList !== null
<ul className="autocomplete-list">
{this.filteredListMarkup()}
</ul>
}
filteredListMarkup() then uses map to return an <li> for each item with the necessary event handlers to update the selected item into the input and close the autocomplete-list by this.setState({filteredList: null});
You might also find this one useful:
https://github.com/reactjs/react-autocomplete
Even if you could use dependencies, I tried a bunch of the top current ones and personally wasn't happy with any of them (added dependencies like jQuery, not lightweight to use/understand/customize, css challenges, etc).
In then end, I found this lightweight vanilla React typeahead tutorial (no, I didn't write the tutorial). It's quick, simple, and three's no added dependency tree weight (eg: jQuery) or dependency maintenance. This solution also easily adjusted to the newer React patterns & the libraries I was using, and I'm guessing the same would be true of the patterns/libraries you may be using. Maybe this will help you or someone else like it did me.