Dynamic number of screens in TabNavigator - reactjs

Situation
I currently write a news reader app for a magazine, which publishes the content in English and German under different categories. The number of categories per language is different. The categories are stored in as an array per language.
CATEGORIES_EN = [
{
selector: '*',
blog: BLOG_EN,
id: `${BLOG_EN}_*`,
},
{
selector: 'Politics',
blog: BLOG_EN,
id: `${BLOG_EN}_Politics`,
},
// ... 8 more
];
CATEGORIES_DE = [
{
selector: '*',
blog: BLOG_DE,
id: `${BLOG_DE}_*`,
},
{
selector: 'Politik',
blog: BLOG_DE,
id: `${BLOG_DE}_Politik`,
},
// ... 9 more
];
The screen component is always the same, but must receive the selector and the blog somehow.
Question
How can I change the number of screens, when the the language has changed?
How can I assign the categories to the screen component?
Environment
react-navigation: 1.0.0-beta.11
react-native: 0.45.0
Git-Issue
https://github.com/react-community/react-navigation/issues/1872

I've found a solution and want to share it with you:
You have to recreate the TabNavigator all the time when something has been changed.
For details please look in my Git-Issue and the referenced issue.

Related

Get a list of components and their props from MDX string - by regex?

My problem - Get components & props from MDX/JSX string
I have an MDX string with Front-matter meta data in YAML, some regular text, some markdown and some React components.
I need to get the list of all React (non-HTML) components from it with their parameters.
So given this example:
---
title: Title of documents
tags: one, two, three
---
# My heading H1
Some text in paragraph. Than the list:
- First item
- Second item
More text with [a link](https://example.com).
<Articles category="theatre" count={3} />
Further text with more information.
<Newsletter categories={['theatre', 'design']} />
<MultilineComponent
paramA="A"
paramB="B"
/>
<ComponentWithChildren param="value">
Some children
</ComponentWithChildren>
... I would need this output:
[
{
component: 'Articles',
props: {
category: 'theatre',
count: 3,
},
},
{
component: 'Newsletter',
props: {
categories: ['theatre', 'design'],
}
},
{
component: 'MultilineComponent',
props: {
paramA: 'A',
paramB: 'B',
}
},
{
component: 'ComponentWithChildren',
props: {
param: 'value',
}
}
]
Also I need to do this on the server, so I don't have access to browser functionality (window, document, etc.).
What I've tried
Some basic Regex, but as I'm far from being professional in regexing, now I have two problems. :)
Is there some built in way how to parse JSX string to get a list of components & props in the way that I've described above? Or is there some maybe some other parser that I can use to solve this? If not, is there some Regex pattern I can use to get this?
Quick summary on "Why"
During the build of my Next.js project I need to determine which data is actually needed for each MDX page in the bundle. So if I see this in the Mdx file:
...other text
<Articles category="theatre" count={3} />
...other text
... which I'm somehow able to parse to this:
component: "Articles"
category: "theatre"
count: 3
that's enough info for me to know that I need to send those data to the page:
[
{
title: 'Romeo and Juliet',
category: 'theatre',
},
{
title: 'The Cherry Orchard',
category: 'theatre',
},
{
title: 'Death of a Salesman',
category: 'theatre',
}
]
Would you be able to help me with this? Thank you in advance! 💪
Edit
#Rango's answer pointed me to the right direction! One caveat: jsx-parser can not handle multiline components to which Rango's proposed the following solution:
if (rsp.test(c)) continue; // add before /index.js:374
This however removes all whitespace from string attributes. So I've replaced it with this:
if (/[\n\r]/.test(c)) continue; // this should remove lines only
So far this solution works. I would be more comfortable to use more stable libraries, but unfortunately none of the proposed solution worked for me (acorn-jsx, react-jsx-parser, babel/parser).
Not sure that parsing JSX with regular expressions is efficient because curly brackets {...} can contain any JS expression, so if you choose this way then prepare to parse Javascript as well.
Fortunately, there are a bunch of JSX parsers that can do it for you. E.g. the first one I picked was jsx-parser and this small beast can parse your example (with a simple trick). The shape of the result is quite different but you can transform it to match your needs.
var test = `
---
title: Title of documents
tags: one, two, three
---
# My heading H1
Some text in paragraph. Than the list:
- First item
- Second item
More text with [a link](https://example.com).
<Articles category="theatre" count={3} />
Further text with more information.
<Newsletter categories={['theatre', 'design']} />
<MultilineComponent
paramA="A"
paramB="B"
/>
<ComponentWithChildren param="value">
Some children
</ComponentWithChildren>
`
const components = [...test.matchAll(/<[A-Z]/g)]
.map(match => JSXParser(test.slice(match.index)))
document.getElementById('result').textContent = JSON.stringify(components, null, 2)
<script src="https://unpkg.com/jsx-parser#1.0.8/index.umd.js"></script>
<pre id="result">Hello</pre>
In my snippet I used UMD version of the package, but for node.js consider choosing ES module ofc.

useFirestoreConnect with populates - usage/implementation

DB Setup:
- users
- A: { // private user info }
- usersPublic
- A: { // public user info }
- rooms
- 1: { readAccess: ['A'] }
I have a component that displays all rooms and am fetching that in the following way:
useFirestoreConnect(() => [{collection: 'rooms'}] )
This is working fine, but I now want to also load in the info from usersPublic for each user in the rooms readAccess array.
I'm attempting to use populates in the following way:
useFirestoreConnect(() => [{
collection: 'rooms',
populates: [{
root: 'usersPublic',
child: 'A'
}]
}])
I'm pretty sure my implementation of populates is wrong and I'm failing to understand exactly how to make this work.
I could return a bunch of other query configs for all users with read access once I have the room object but that seems inefficient and it seems that populates is meant to solve exactly this problem.
I'm also open to suggestions on modeling the DB structure - the above made sense to me and offers a nice separation between private/public user info but there might be a better way.
The way to do it is:
populates: [{ root: usersPublic, child: 'read_access ' }]
This results in a redux state that looks like:
...etc
data: {
rooms: { ... rooms ... }
usersPublic: { A: { ... usersPublic[A] ... }, etc }
}

How to model recursively nested data in state

I have a data structure typed like:
export interface IGroup {
id: number;
name: string;
groupTypeId: number;
items: IItem[];
groups: IGroup[];
}
Which recursively represents many to many relationships between a "Group" and a "Group" and an "Group" and an "Item". Groups are made up of items and child groups. An item derives to just a simple type and other meta data, but can have no children. A single group represents the top of the hierarchy.
I currently have components, hooks, etc to recursively take a single group and create an edit/create form as shown below:
I have this form "working" with test data to produce a standard data output as below on save:
{
"1-1": {
"name": "ParentGroup",
"groupType": 2
},
"2-4": {
"name": "ChildGroup1",
"groupType": 1
},
"2-9": {
"name": "ChildGroup2",
"groupType": 3
},
"2-1": {
"itemType": "FreeForm",
"selectedName": "Testing",
"selectedClass": 5
},
"2-2": {
"itemType": "FreeForm",
"selectedName": "DisplayTest",
"selectedClass": 5
},
"3-4": {
"itemType": "EnumValue",
"selectedItem": {
"id": 12900503,
"name": "TRUE"
}
},
"3-5": {
"itemType": "EnumValue",
"selectedItem": {
"id": 12900502,
"name": "FALSE"
}
},
"3-9": {
"itemType": "FreeForm",
"selectedName": "Test",
"selectedClass": 5
},
"3-10": {
"itemType": "FreeForm",
"selectedName": "Tester",
"selectedClass": 5
},
"3-11": {
"itemType": "FreeForm",
"selectedName": "TestTest",
"selectedClass": 5
}
}
The "key" to these objects are the grid column and row since there are no other guaranteed unique identifiers (if the user is editing, then it is expected groups have ids in the db, but not if the user is adding new groups in the form. Otherwise, the name is an input form that can be changed.) It makes sense and it is easy to model the keys this way. If another group or item is added to the hierarchy, it can be added with its column and row.
The problem that I have is that I would love to be able to have an add button that would add to a groups items or group arrays so that new rows in the hierarchy could be created. My forms should handle these new entries.
Ex.
"1-1": {
groups: [..., {}],
items: [..., {}]
}
But the only data structure that I have is the IGroup that is deeply nested. This is not good for using as state and to add to this deeply nested state.
The other problem I have is that I need to be able to map the items and groups to their position so that I can translate to the respective db many to many tables and insert new groups/items.
Proposed solution:
I was thinking that instead of taking a group into my recursive components, I could instead create normalized objects to use to store state. I would have one object keyed by column-row which would hold all the groups. Another keyed by column-row to hold all the items. Then I think I would need two more objects to hold many to many relationships like Group to Group and Group to Item.
After I get the data from the form, I hopefully can loop through these state objects, find the hierarchy that way and post the necessary data to the db.
I see that this is a lot of data structures to hold this data and I wasn't sure if this was the best way to accomplish this given my modeling structure. I have just started using Redux Toolkit as well, so I am somewhat familiar with reducers, but not enough to see how I could apply them here to help me. I have been really trying to figure this out, any help or guidance to make this easier would be much appreciated.
Go with normalizing. Each entity having a single source of truth makes it much easier to read and write state.
To do this, try normalized-reducer. It's a simple higher-order-reducer with a low learning curve.
Here is a working CodeSandbox example of it implementing a group/item composite tree very similar to your problem.
Basically, you would define the schema of your tree:
const schema = {
group: {
parentGroupId: { type: 'group', cardinality: 'one', reciprocal: 'childGroupIds' },
childGroupIds: { type: 'group', cardinality: 'many', reciprocal: 'parentGroupId' },
itemIds: { type: 'item', cardinality: 'many', reciprocal: 'groupId' }
},
item: {
groupId: { type: 'group', cardinality: 'one', reciprocal: 'itemIds' }
}
};
Then pass it into the library's top-level function:
import normalizedSlice from 'normalized-reducer';
export const {
emptyState,
actionCreators,
reducer,
selectors,
actionTypes,
} = normalizedSlice(schema);
Then wire up the reducer into your app (works with both React useReducer and the Redux store reducers), and use the selectors and actionCreators to read and write state.

How to get the leaflet-control-layers-toggle icon to show with angular-leaflet-directive

Following instructions as per Layers Simple example on tut: http://tombatossals.github.io/angular-leaflet-directive/#!/examples/layers-simple
Inclusion of below successfully loads on page, yet icon in top right to toggle different layers is not produced. No direction provided on repo as to ensure that it is produced.
$scope.layers = {
baselayers: {
osm: {
name: 'OpenStreetMap',
url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
type: 'xyz'
},
mapbox_light: {
name: 'Foo',
url: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
type: 'xyz'
}
}
},
That seems to be a bug in the current 0.8.1 version of angular-leaflet-directive. Today I had the same problem. Going back to 0.7.15 and Layer Control is visible again.

How to post many-to-many relational data to a restful api from AngularJS?

I have a Theme Management module in my web application. I'ö using SequelizeJS in server side.
Models are:
module.exports = function(sequelize, DataTypes) {
var Theme = sequelize.define('Theme', {
name: DataTypes.STRING,
description: DataTypes.STRING
}, {
associate: function(models) {
Theme.belongsToMany(models.Option, { through: models.ThemeOptions })
},
tableName: 'themes'
});
return Theme;
};
module.exports = function(sequelize, DataTypes) {
var ThemeOptions = sequelize.define('ThemeOptions', {
}, {
tableName: 'theme_options'
});
return ThemeOptions;
};
module.exports = function(sequelize, DataTypes) {
var Option = sequelize.define('Option', {
key: DataTypes.STRING,
value: DataTypes.STRING
}, {
associate: function(models) {
Option.belongsToMany(models.Theme, { through: models.ThemeOptions })
},
tableName: 'options',
timestamps: false
});
return Option;
};
In /#/themes/create state, I want to create a theme with some options, like color codes.
I am creating a theme with
$http.post('/themes', themeData)
then with it's it, I am creating options. Finally I should post many-to-many data to theme options. So for a theme that has 10 options, I am posting 21 times.
What is the best way to post many-to-many data to a REST server?
Don't really know how you handle your routing or backend. Supposing you're using express and your options are previously created, I'd recommend creating a new post route for handling each ThemeOptions
app.post('/themeOptions', { ThemeId: 1, OptionId:2 });
and use that info to create a ThemeOptions instance to join a Theme with certain Option.
This could reduce your post quantity to the half + 1 (one for the Theme and one for each ThemeOption).
Another solution is to maybe manage an array of ThemeOptions and use ThemeOptions#bulkCreate to create them at once, using only 2 posts (one for the Theme and one for all the ThemeOptions.
Would be something like this:
app.post('/themeOptions', {
options: [{
ThemeId: 1,
OptionId:2
}, {
ThemeId: 1,
OptionId:3
}
// and so on...
});
Each of these solutions could involve more logic to manage each front end request, but could increase front end behavior as well.
A final (and more complex at the backend) solution would be to send a unique post sending both, the Theme and the Options array, and create all the ThemeOptions after creating the Theme
// frontend
app.post('/theme', {
theme: {
name: 'John',
description: 'Doe'
},
options: [2, 3 /* and so on ... */]
});
// backend
Theme.create(req.body.theme).on('success', function (theme) {
var options = req.body.options.map(function (option) {
return {
ThemeId: theme.id,
OptionId: option
};
});
ThemeOptions.bulkCreate(options);
})
In the Symfony2 world, there is a bundle, SonataAdminBundle, that generates admin interfaces. With the entity classes (here, Option and Theme), it generates all the pages, listing, creating and editing theses entities. It generates forms that handles many to many relationship. Here how it manages that :
The user consults the creation/edition form of the entity of any side of the ManyToMany relation. In the form, where it have to display the many to many association, it displays a <select>, with Select2 for instance, which is a <select> with some jQuery. Each element of the list is linked with the corresponding ID in the database, something like <option value="13456">Option #3</select>. For a many to many relationship, we can select multiple fields at the time. Internally, it builds an array of Option IDs with the <select>.
If we want to add a inverse entity on the fly (here, the inverse entity is Option, I think ...), there is a button that open ups an Option creation form, and once the new Option is added, it adds the newly created option in the <select>, so the user can add it in the form immediately.
Then, it sends the array of Option ID's built with the form.
I think this strategy could fit your needs.

Resources