I have an app which is very similar to instagram, but instead of having one main feed, the user
can have multiple feeds based on the events he/she attends. I'm using redux for the state management
and currently I have these reducers:
feed
people
schedule
user
navigation
My feed reducer looks like:
{
allEvents: [],
event: {
id: 123,
name: 'Foo'
},
items: [
{
id: 23,
imageUrl: 'http://img.com/foo',
likes: [{ id: 1, user: {...} }, { id: 2, user: {...} }],
comments: [{ id: 1, text: '...', user: {...} }, { id: 2, text: '...', user: {...} }]
}
]
}
So my state structure currently looks like:
{
feed,
people,
schedule,
user,
navigation
}
But at the moment every time the user changes current event the whole feed state is replaced by a
new state for that particular event, so if the user comes back to the previous event the whole feed needs
to be fetched again, same is with people reducer and schedule, which depends on the event.
Also user has it's own profile feed which shows user's feed items. And in order to have this feed I would
need to duplicate what I have in the current feed reducer, so I thought it would be better to have multiple feeds,
inside event reducer.
I was wondering if the state structure like this wouldn't be better:
{
events: {
items: [
feed,
people,
schedule
]
}
user,
navigation
}
Then I read redux#815 or redux#994 that it's not the best way to nest reducers.
Should it look more or less like:
{
feed: {
feedIds: [23, 24, 25],
byId: {
23: {
items: [123, 124, 125]
}
}
},
items: {
itemsIds: [123, 124, 125, 126, 127],
byId: {
124: {
id: 124,
image: 'http://img.com ',
likes: [1, 2, 3]
}
}
},
likes: {
likesIds: []
},
events: {
eventIds: [1, 2, 3],
byId: {
1: {
id: 1,
name: 'TYW Croatia w34'
feedId: 23,
peopleId: 12,
scheduleId: 1
}
}
},
people: {...}
}
What's the best practice in this case and which way is the most performant?
A normalized structure, like your last example, is definitely both a best practice and more performant. The state is flatter, so updates are more targeted and affect fewer unrelated objects; items can be easily looked up by ID as needed; and the update logic will generally be simpler. Also, this allows you to pass item IDs to connected child components, which then look up their own data based on that ID, and they will only need to re-render when their own data changes. Finally, it works well for caching data.
You might want to read through some of these articles on Redux performance for more information, particularly High Performance Redux. You may also want to read some of the discussion at Normalising state and garbage collection.
edit:
As a follow-up, I recently added a new section to the Redux docs, on the topic of "Structuring Reducers". In particular, this section includes chapters on "Normalizing State Shape" and "Updating Normalized Data".
Related
I have been trying to change the language from English to others,
$t('bascketball')
↑this works inside the template, however I am looking the way that I can change the language of elements inside of export Default.
If you know how to solve it, please advice me.
export default {
name: "Home",
components: {},
data() {
return {
Games: [
keyword: '',
games: [
{
heading: $t('Basketball'),
text:
"Hello Basketball players, want to know about team members. Click Below.",
img:
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQt0Sh97iYcu0kTguhcsW_szWfzolqu1ynGeQ&usqp=CAU",
},
You can do it with one of these:
this.$t('Basketball')
this.$i18n.tc('Basketball')
But because you're calling the API, you cannot do it in data, you can rewrite it to a computed method, like that:
computed: {
games() {
return [
{
heading: this.$t('Basketball'),
text:
"Hello Basketball players, want to know about team members. Click Below.",
img:
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQt0Sh97iYcu0kTguhcsW_szWfzolqu1ynGeQ&usqp=CAU",
}
]
}
}
I have particular state object which consist of state object which looks like this.
{
cards:[
{
id:'card01',
title:"My card 01",
category:[
{
id:1,
name:'cat1',
tags:[{id:11,name:'tag1'}]
},
{
id:2,
name:'cat2',
tags:[{id:11,name:'tag1'}]
}
]
},
{
id:'card01',
title:"My card 02",
category:[
{
id:11,
name:'cat11',
tags:[{id:111,name:'tag11'}]
},
{
id:22,
name:'cat22',
tags:[{id:111,name:'tag22'}]
}
]
}
]
}
This particular object is fetched from the backend and it is kept in the store. I need to filter out the categories when ever the user selects a card (when a card is selected it returns of gets the card id). and i need to display the category in a different component with the tags inside the category. Do i need to use reselect for this is there any advantage of using it?. I need to to do this filtration on the data in my store.
I have a base component within which I have a dynamic component with a v-for that displays based on a computed property.
All I've really tried doing thus far, which was an incorrect methodology, was to wrap the method that loads data in a settimeout. This question is as much a methodology question as it is a coding question.
My base component looks like this:
<template>
<div>
<v-progress-linear
v-model="progressValue"
v-if="loading"
></v-progress-linear>
<component
v-for="table in tables"
:key="table.id"
:is="table.structure"
:table="table"
></component>
</div>
</template>
<script>
import Annual from './DataTables/Annual';
import { mapState, mapGetters } from 'vuex';
export default {
name: "Page",
props: [],
components: {
Annual,
},
data: () => ({
progressValue: 0,
loading: false,
tables: [],
}),
computed: {
...mapGetters({
currentTables: 'getCurrentPageTables',
tableTitles: 'getCurrentPageTableTitles',
}),
...mapState({
pageName: state => state.pageName,
snakeName: state => state.snakeName,
}),
methods: {
updateTables(payload) {
this.loading = true;
payload.forEach(title => {
this.tables.push(this.currentTables.filter(e => title === e.name)[0]);
this.progressValue = this.tables.length / payload.length;
})
},
},
watch: {
snakeName: {
handler() {
this.progressValue = 0;
this.updateTables(this.tableTitles);
this.$nextTick(() => {this.loading = false;})
},
immediate: true,
},
}
}
</script>
Annual.vue is simply a component that displays a Vuetify v-data-table element and its structure is fairly inconsequential to this.
For all intents and purposes we can consider currentTables and tableTitles to both be arrays, the first of objects whose data populate the v-data-tables in Annual.vue, and the second of strings which are just the names of the tables.
When the user navigates to another page the getters return different data, based on the page the user navigates to, but some of the pages have over 20 tables, which makes page loading slow upon navigation to these pages. I am trying to do one of two things:
1. Asynchronously load the components one at a time while still making the page functional for the user to navigate through.
2. Display a loader that disappears after all of the content is rendered. I'm having trouble figuring out how to do the latter because I can't put this functionality into the mounted() hook since all of this happens upon the watched parameter changing (hence the component is not re-mounted each time the route changes).
Any advice on how to tackle this would be appreciated.
I'm developing a Wordpress Plugin that allows users to add custom video playlists to their pages or posts, and for that I'm using Videojs and Videojs Playlist libraries.
I've successfully managed to add a single playlist into a page, but when a second one is created the first player is disabled.
First Player disabled
Other problem I'm facing is that, although the vjs-playlist div is added, it only shows in the first player created.
Code display in the browser
var options = {"techOrder": ["html5","youtube", "flash"]};
var myPlayer = videojs('my-playlist-player', {options, "autoplay": false, "controls": true, "fluid": true, "liveui": true});
myPlayer.playlist([{
name: 'Test item 1 type .m3u8',
description: 'Testing for videojs-playlist-ui integration',
duration: '45',
sources: [
{src: '//vjs.zencdn.net/v/oceans.mp4',type: 'video/mp4'}
],
thumbnail: [
{
srcset: '//bcvid.brightcove.com/players-example-content/clouds-1800.jpg',
type: 'image/jpeg',
style: 'max-height: 120px;'
}
]
},{
name: resTitle,
description: resDesc,
duration: resDuration,//'45',
sources: [
{src: resItemSrc, type: resMime}
],
thumbnail: [
{
srcset: resThumbnail,
type: resImgType,
style: thumbStyle//'max-height: 120px;'
}
]
}
}
]);
console.log(myPlayer.playlist());
myPlayer.playlistUi({el: document.getElementById('vjs-playlist'), horizontal: true});
myPlayer.playlist.autoadvance(1);
I believe my errors happen because videojs functions are detecting the same id in all elements, but if so how could I avoid this?
Any other ideas or opinions on why this errors might be happening, would be great.
Thanks in advance.
A bit more of information, I wanted to check how many players the script detected and so I printed in the console the window.videojs.players and only one player is detected even if more are created. Check result
Could this be because they have the same ID? If so how could it be fixed? HTML Result
I'm trying to simulate the clicking of CSS elements on my page and automatically take screenshots of the window at each stage for testing purposes. I'm using backstopJS as the CSS testing/screenshot framework. Everything seems to work fine for this first element. A modal is triggered when i click on the register link in the main header menu. but it is not generating any reference screenshotof the modal.
plz help to trigger a reference screenshot of the modal in the below given script
This is the script :
{
"viewports": [
{
"name": "desktop",
"width": 1600,
"height": 900
},
{
"name": "phone",
"width": 320,
"height": 480
},
{
"name": "tablet_v",
"width": 568,
"height": 1024
},
{
"name": "tablet_h",
"width": 1024,
"height": 768
}
],
"grabConfigs" : [
{
"testName":"vawizard"
,"url":"http://localhost/vawizard/index.html"
,"hideSelectors": [
]
,"removeSelectors": [
"header.banner--clone"
]
,"selectors":[
"#outer_wrapper header"
,".banner_box"
,".help_desk"
,".big_search_box"
,".look_specific"
,".smart_tool_box"
,"footer"
,".copyright_box"
]
}
]
}
This is the link
http://wizard.hodgesdigital.com/
Any ideas what could be causing this behavior?
BackstopJS is mainly focused on testing layout states at different screen sizes and doesn't support the testing of user interactions.
In your case I can make two recommendations. 1) You could add a state to your URL which triggers the modal when your page is loaded OR 2) you could write a custom CasperJS script to test cases like this which require some user interaction. More detail below...
Approach 1:
In the first case you could add a hash to your URL which would trigger your modal, In my experience it's common for web apps (e.g. Angular and Ember) to represent modal states in this way....
// in your BackstopJS config
"url": "http://localhost/vawizard/index.html#openModal",
// then as part of a jQuery script
$(document).ready(function(){
if ( /openModal/.test(location.hash) )
{
// Do your open modal action here
// Then trigger BackstopJS
}
});
Approach 2:
If the above is not your style there is another good option. As part of the BackstopJS install you also have a full version of CasperJS -- so I would recommend going to CasperJS.org to look at some basic examples and see if it makes sense to write your own scripts. It will be a little more time consuming in the short run but the project specific boilerplate you write now may be nice to have for future edge cases (for testing forms, other interactions etc.).
We use custom scripts for such things. e.g
module.exports = async(page, scenario, vp) => {
await require('./onReadyInfo')(page, scenario, vp);
await require('./clickAndHoverHelper')(page, scenario, vp);
await require('./onReadyWaitForImages')(page, scenario, vp);
await page.waitForSelector("div[data-social-media-source='instagram'] a[data-target='#social-media-settings']");
await page.click("div[data-social-media-source='instagram'] a[data-target='#social-media-settings']");
await page.waitForSelector(
"#checkbox-twitter", {
timeout: 6000
}
);
await page.waitForTimeout(500);
await page.evaluate(async() => {
jQuery("#checkbox-twitter + label").click();
setTimeout(() => {
jQuery('#checkbox-twitter + label').focus()
}, 500);
});
await new Promise(resolve => setTimeout(resolve, 300));
};
This will open a dialog on link click, check a checkbox and give the label focus.