I am working on moving all my core business logic to a model layer, which would be used across all my projects.
My project set mainly comprises of a web client-facing application built on Reactjs, few internal tools also built on Reactjs, and a mobile application built on react-native.
In my model layer, I have created custom data types to handle null/empty scenarios from backend and also to add custom format functions.
Following is the code for a string model which I have built.
/**
Author - Harkirat Saluja
Git - https://bitbucket.org/salujaharkirat/
**/
"use strict";
class CustomString {
static init (value = "") {
if (value === null) {
value = "";
}
const s = new String(value);
s.__proto__.upperCaseFormat = function () {
return this.toUpperCase();
};
return s;
}
}
export default WealthyString;
The way I invoke this is as follows:-
const firstName = WealthyString.init(firstName);
const lastName = WealthyString.init(lastName);
Now, if we see this returns a string object.
In my web project, I use this as follows in the react component, and it works nice and fine.
<span>{firstName}{" "} {lastName}</span>
But, in my react-native project, if I use it in the same way, it throws this error. Also, this error only comes when remote debugging if off and not when I am connected to chrome debugger.
<Text>{firstName}{" "} {lastName}</Text>
So, in order to resolve this for now, where strings are appended as the way shown above I have used toString(). But I was wondering is there something wrong the library or am I missing something?
Update
So looks like String objects are not working with Text in react-native at all. So to fix this I do the following:-
const SecondaryText = ({style, children}) => {
const styleCopy = addLineHeightToStyle(style, 14);
let dupChildren = children;
if (dupChildren instanceof String) {
dupChildren = dupChildren.toString();
}
return (
<ReactNative.Text
allowFontScaling={false}
style={[styles.secondaryText, styleCopy]}
>
{dupChildren}
</ReactNative.Text>
);
};
Built a wrapper over Text which react-native provides and convert the object to a string inside this.
Concatenate string using template literals in case of string combination.
Consider use template literals for complicated string. Eg:
<Text>{`${firstname} ${lastname}`}</Text>
Do you want to change this as follows?
<Text>{firstName +" "+lastName}</Text>
There are multiple ways you can do this. Write a function and return the concatenated string.
const test = (firstName ,lastName) => {
return firstName+lastName
}
If you have a class, you can do like this inside your render function.
<Text>{this.test()}</Text>
Related
I'm just doing a bit of refactoring and I was wondering if I have a bunch of useCallback calls that I want to group together, is it better do it as a simple hook that I would reuse in a few places?
The result would be
interface IUtils {
something(req: Something) : Result;
somethingElse(req: SomethingElse) : Result;
// etc...
}
So a plain hooks example would be:
export function useUtils() : IUtils {
// there's more but basically for this example I am just using one.
// to narrow the focus down, the `use` methods on this
// block are mostly getting data from existing contexts
// and they themselves do not have any `useEffect`
const authenticatedClient = useAuthenticatedClient();
// this is a method that takes some of the common context stuff like client
// or userProfile etc from above and provides a simpler API for
// the hook users so they don't have to manually create those calls anymore
const something = useCallback((req:SomethingRequest)=> doSomething(authenticatedClient), [authenticatedClient]
// there are a few of the above too.
return {
something
}
}
The other option was to create a context similar to the above
const UtilsContext = createContext<IUtils>({ something: noop });
export UtilsProvider({children}:PropsWithChildren<{}>) : JSX.Element {
const authenticatedClient = useAuthenticatedClient();
const something = useCallback((req:SomethingRequest)=> doSomething(authenticatedClient), [authenticatedClient]
const contextValue = useMemo({something}, [something]);
return <UtilsContext.Provider value={contextValue}>{children}</UtilsContext.Provider>
}
The performance difference between the two approaches are not really visible (since I can only test it in the device) even on the debugger and I am not sure how to even set it up on set up on jsben.ch.
Having it as just a simple hook is easier I find because I don't have to deal with adding yet another component to the tree, but even if I use it in a number of places I don't see any visible improvement but the devices could be so fast that it's moot. But what's the best practice in this situation?
We are in the process of integrating RTK query in our app.
Our current "architecture" is as follow:
All the business logic actions are written inside services which are plain JS classes.
Those services are passed using react context in order for the component tree to be able to call services functions.
As of now those services were accessing the redux store directly to perform the appropriate logic.
Now that we are moving to RTK, accessing the RTK cache from a service is less trivial:
As far as I can see, the only way to access it is via the select function of the relevant endpoint.
The point is that this method is a "selector factory" and using it outside of a react component doesn't seems to be the right way to go.
Here is an exemple:
class TodoService {
getTodoTitle( todoId: string ) {
// This doesn't looks the right way to do it
const todoSelector = api.endpoints.getTodo.select( {id: todoId } );
const todo = todoSelector( state )
return todo.data.title
}
}
Is there any way to implement safely the following code
class TodoService {
getTodoTitle( todoId: string ) {
// Is there any way to do that kind of call ?
const todoEntry = api.endpoints.getTodo.getCacheEntry( {id: todoId } );
return todoEntry.data.title
}
}
I guess that the answer is "no" and I have to refactor our whole architecture, but before doing so I'd like to be sure that there is no alternate approach.
Note that I could build the cache entry key by myself, but that also doesn't sound like a robust approach...
The thing is that you don't want to just get the cache entry - the selector does a little more for you than just that.
So let's just stay with "please use the selector" and "we won't add another way of doing that" because, selectors is how your code should interact with Redux all the time - React or not.
If you are not calling this code from React where you would need a stable object reference, it is good as it is. The selector factory will create a new selector and thus you get an un-memoized result. This is not perfect, but if you are not relying on referential equality, it also does not hurt at all.
If you want to have the referential equality, you'll have to store the selector in some way, probably as a class property.
Something like this would be possible:
class TodoService {
getSelectorForTodo(todoId: string) {
if (this._lastId !== todoId)
this._lastSelector = api.endpoints.getTodo.select( {id: todoId } )
return this._lastSelector
}
getTodoTitle( todoId: string ) {
const todo = this.getSelectorForTodo(todoId)(state)
return todo.data.title
}
}
I tried to switch to Jest-testing inside of our React application.
Lots of mocha and enzyme tests are working with small fixes.
With tests for a currency formatting component I get the wrong culture format inside the tests. I expect the result to be in format:
1.234.567,89 €
but I receive:
1,234,567.89 €
In the application I get all values formatted nicely in German formats as expected.
BTW:
Inside of a helper method we use following code for internal formatting
value.toLocaleString(['de-DE'], {
style: 'decimal',
useGrouping: true,
minimumFractionDigits: 2,
maximumFractionDigits: 2
})
I've got it...
Because Jest is using Node for testing and Node doesn't support other locales than 'en', I need to add Intl/IntlPolyfill inside a Jest setup file.
const areIntlLocalesSupported = require('intl-locales-supported');
const localesMyAppSupports = [
'de'
];
if (global.Intl) {
// Determine if the built-in `Intl` has the locale data we need.
if (!areIntlLocalesSupported(localesMyAppSupports)) {
// `Intl` exists, but it doesn't have the data we need, so load the
// polyfill and patch the constructors we need with the polyfill's.
const IntlPolyfill = require('intl');
Intl.NumberFormat = IntlPolyfill.NumberFormat;
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;
Number.prototype.toLocaleString = IntlPolyfill.__localeSensitiveProtos.Number.toLocaleString;
Date.prototype.toLocaleString = IntlPolyfill.__localeSensitiveProtos.Date.toLocaleString;
}
} else {
// No `Intl`, so use and load the polyfill.
global.Intl = require('intl');
}
I don't know the reason, but I need to explicitly overwrite Number.prototype.toLocaleString and Date.prototype.toLocaleString.
Can't find any relevant information in the sencha documention about this question :
Is it possible to call Ext.create(...) with a parameter which does not depend on the application's name?
So that if I change the app's name I don't have to rewrite that line of code?
Normally I would use Ext.create(AppName.model.MYMODEL) but that's too tied to the app's name for me.
Still need help :)
Create using class alias
When using Ext.define to define your class, you can provide an alias property. You've probably seen this on UI components which use aliases like widget.panel. Those aliases can be used with Ext.create.
Ext.define('MyApp.SomeClass', {
alias: 'app.someclass', // Independent of class name
/* ... */
});
Ext.create('app.someclass', {
/* ... */
});
You can set the alias on a class after it has been created by using Ext.ClassManager.setAlias.
Helper function using application name
If you don't have the option to set an alias, you could create a function that wraps Ext.create which supplies your base namespace automatically.
The problem here is that Ext.application doesn't return the application object. I'm not sure how Sencha Architect generates the application code but you may need additional overrides to allow you to retrieve the application object.
function appCreate(className, config) {
var appName = someMethodThatGetsTheApplicationName();
return Ext.create(appName + '.' + className, config);
};
// Example usage: Creates object of MyApp.model.MyModel
var myObj = appCreate('model.MyModel', { /* ... */ });
How to get the application name at runtime
By default, Ext JS does not retain a reference to the application object when using Ext.application, so we need an override to do it. I'm using Ext.currentApp as the property to store this object, but you can change it to whatever you'd like.
Ext.application = function (config) {
Ext.require('Ext.app.Application');
Ext.onReady(function () {
Ext.currentApp = new Ext.app.Application(config);
});
};
Now that you have this, you can access the application name by simply using Ext.currentApp.name. Or, if you'd feel more comfortable using a getter you can use the following.
Ext.app.Application.addMembers({
getName: function () {
return this.name;
}
});
// Example usage:
function someMethodThatGetsTheApplicationName() {
if (!Ext.currentApp) {
Ext.Error.raise('Current app does not exist.');
}
return Ext.currentApp.getName();
}
You can use any class name in Ext.create there is no naming convention imposed there as long as the class was already defined. If you want Ext.create to load the correct file using Ext.loader you will need to configure the loader to conform with the naming convention you need.
The way to do it :
You need a controller that will in it's INIT function (before UI Loading/Initiating) do the following
APPNAME = this.getApplication().getName();
Where APPNAME is a global variable.
Then when you Ext.create something you will be able to write the following
Ext.create(APPNAME +'model.MyModel');
That way you can change you app name without having to check everywhere in your code to change every single Ext.create to the new app's name.
It also give you the ability if you are to use this.getApplication().setName() to have infinite cache storage has you get 5/10mb per AppName.
I imagine this is an easy thing to do, but I wasnt able to find the information I was looking for through google. I have popupProperties which is just default stuff. I then call to the service which returns specific overrides depending on the popup. How can I iterate through all of the service's overrides and apply them to the popupProperties?
var popupProperties = getDefaultPopupProperties();
var popupOverrides= popupService.getPopupOverrides(currPopupId);
angular.forEach(popupOverrides, function(popupProperty, propertyName){
//replace defaults with popupData's properties
});
You should have a look at the solution of Josh David Miller which uses the extend method of angular (documentation).
var defaults = {name:'John',age:17,weight:55};
var overrides = {name:'Jack',age:28,color:'brown'};
var props = angular.extend(defaults, overrides);
// result
props: {
name:'Jack',
age:28,
weight:55,
color:'brown'
}
The values are copied in the defaults variable. There is no need of using the return value (var props =).
I presume you mean both functions are returning objects with a number of properties (as opposed to an array).
If so, the following should work - just JavaScript, nothing AngularJS specific:
for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }
See this question for more details How can I merge properties of two JavaScript objects dynamically?