Spread a destructuring to avoid a duplication - ecmascript-6

Is there a way to avoid the duplication of competenceList[competenceKey] (object) in lines :5 and :6, keeping the same object on return without using variable declaration?
const func = ({
entities: { competence: competenceList },
timesheet: { management: { competences: competenceKey } },
}) => ({
employeeKey: competenceList[competenceKey].employee,
payrollEnd: competenceList[competenceKey].competenceEnd,
});

Yes, it is technically possible by using a computed property name:
const func = ({
timesheet: { management: { competences } },
entities: { competence: { [competences]: competence },
}) => ({
employeeKey: competence.employee,
payrollEnd: competence.competenceEnd,
});
but I wouldn't recommend this as it's not really readable. Just write
function func({timesheet, entities}) {
const competence = entities.competence[timesheet.management.compentences];
return {
employeeKey: competence.employee,
payrollEnd: competence.competenceEnd,
};
}

Related

Why useReducer does not update state in react-typescript

I have a problem with Typescript with React and useReducer hook. The state does not wanted to be updated and it is 100% typescript problem (I mean I need a typescript solution because in javascript it works perfect). So I want to make reducer as short as possible so I use "name" in HTML and get this name as a name of object in initialState. When I return ( title:{}, description: {}) It works but when I use [action.field] it does not work. action.field is the name of HTML element.
const initialStateReducer: inputsFormState = {
title: {
val: "",
isValid: false,
},
description: {
val: "",
isValid: false,
},
};
const RecipeForm = () => {
const inputReducer = (
state: inputsFormState,
action: inputsFormAction
): inputsFormState => {
console.log(action.type, action.content, action.field);
let isValid: boolean;
const { content } = action;
isValid = content.length > 0;
return {
[action.field]: {
val: content,
isValid: isValid,
},
...state,
};
};
const [formIsValid, setFormIsValid] = useState<boolean>(false);
const [inputsValues, dispatchReducer] = useReducer(
inputReducer,
initialStateReducer
);
const changeTextHandler = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
dispatchReducer({
type: ActionKind.changeVal,
field: e.target.name,
content: e.target.value,
});
};
return (
<React.Fragment>
<Input
name="title"
onChange={(e) => changeTextHandler(e)}
placeholder="Name for your recipe"
/>
<Textarea
name="description"
onChange={(e) => changeTextHandler(e)}
placeholder="Description"
cols={20}
rows={20}
resize="none"
/>
</React.Fragment>
);
};
Typescript is only a superset of JS that adds on Typing while writing code, it has no effect on the actually running of the JS (as it gets compiled into JS before running).
From looking at the above code I think the issue is your return in the reducer:
return {
[action.field]: {
val: content,
isValid: isValid,
},
...state,
};
Should be:
return {
...state,
[action.field]: {
val: content,
isValid: isValid,
}
};
As (and this may not be a great explanation) but right most arguments overwrite the preceding values, so you're effectively overwriting the new with the original.

next-i18next i18n on array of objects

I have an array of objects called "Options", that I use as a prop to a dropdown/select Material-UI component. I want to use the next-i18next library on the labels. I already implemented with success through all the next app just like the documentation explains. I tried using the {t('key')} and it doesn't allow.
import { useTranslation } from 'next-i18next'
const UsersPage = () => {
const { t } = useTranslation('user');
const Options = [
{ value: 'fullName', label: 'Nome' },
{ value: 'cpf', label: 'CPF' },
{ value: 'id', label: 'PadrĂ£o' },
]
...rest of the component
}
export const getStaticProps = async ({ locale }) => ({
props: {
...await serverSideTranslations(locale, ['user', 'home']),
},
})
export default UsersPage;
The msefer answer is right:
`${t("key")}`
inside JSON or string building in props like
const since = `${t('since')}`;
const until = `${t('until')}`;
...
<ListItemText
primary={value.name}
secondary={since + value.beginDate + until + value.endDate}
/>

How do I make a validator for a checkbox using a formBuilder Array in angular 9?

I need to validate that my user selects at least one viewport. I simply added the validator.required for the input but for the checkbox in an array I am not sure how to validate it.
public createForm(): FormGroup {
return this.formBuilder.group({
urlInput: ['', [ Validators.required, Validators.pattern(URL_VALIDATOR) ]],
children: [false, []],
viewport: this.formBuilder.array(this.viewports.map(x => false))
});
}
I used this to create the checkbox:
public ngOnInit(): void {
this.urlForm = this.createForm();
const checkboxControl = this.urlForm.controls.viewport as FormArray;
You could create a custom validator for your form:
Validator:
export function minimumNeededSelected(
controlArrayName: string,
quantityNeededSelected: number
) {
return (formGroup: FormGroup) => {
const controlArray = formGroup.controls[controlArrayName];
let quantitySelected = 0;
(controlArray as FormArray).controls.forEach(control => {
if (control.value) {
quantitySelected++;
}
});
if (quantitySelected < quantityNeededSelected) {
controlArray.setErrors({ minimumNeededSelected: true });
} else {
controlArray.setErrors(null);
}
};
}
How to use:
public createForm(): FormGroup {
return this.formBuilder.group({
urlInput: ['', [ Validators.required, Validators.pattern(URL_VALIDATOR) ]],
children: [false, []],
viewport: this.formBuilder.array(this.viewports.map(x => false))
},
{
validators: [minimumNeededSelected('viewport', 1)]
});
}
This way your formArray will get an error of 'minimumNeededSelected' if there is not enough items selected.
You can create a function that returns if a viewport is checked or not.
hasCheckedItem(): boolean {
const viewports = this.urlForm.get('viewport') as FormArray;
if (viewports) {
viewports.controls.forEach(control => {
if (control.value) {
return true;
}
});
}
return false;
}
You can then perform this check before a form submit or next action taken where this needs to be true.

react native: Not able to parse json params

I created a class to get api.
export default class ProductDetail extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
product : []
}
}
componentDidMount() {
this.getProductRequest();
}
...
then create getProductRequest function:
async getProductRequest() {
let response = await fetch('https: ...
let json = await response.json();
console.log(json);
this.setState({ product : json.data});
}
the console result is:
{id: 225782, title: "test", images: Array(1), price: "1$"}
Now in render i get same result:
render() {
console.log(this.state.product);
return (...
Now I try to read params:
render() {
console.log(this.state.product.title);
return (...
But I get this error:
TypeError: Cannot read property 'title' of underfined
what's the wrong?
Edit: Structure:
export default class ProductDetail extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
product : []
}
}
componentDidMount() {
this.getProductRequest();
}
render() {
console.log(this.state.product.title);
return (
<View> <Text style={styles.name}>title</Text></View>
);
}
async getProductRequest() {
try {
let id = this.props.navigation.state.params.productId;
let response = await
fetch('https://www.example.com/product', {
method : 'POST',
headers : {
'Accept' : 'application/json',
'Content-Type' : 'application/json'
},
body : JSON.stringify({
id
})
});
let json = await response.json();
//json: {"data":{id: 225782, title: "test", images: Array(1), price: "1$"},"status":"success"}
this.setState({ product : json.data});
} catch(error) {
//console.log(error)
}
}
}
...
Because, componentDidMount() re-render after the first execution of rendering. So, when you are putting console.log(this.state.product.title); in the render before return, it doesn't get the title param first time.
After the re-render, the value will be available. So, if you want to check the output put console.log elsewhere or just remove it
Edit
You can call this.getProductRequest(); in componentWillMount() instead of componentDidMount()
componentWillMount() {
this.getProductRequest();
}
let product = JSON.parse(this.state.product
if(product.title){
console.log(product.title)
}
Let with above code. If you are getting string in your state, it may create an issue. Let me know if its work.
As said react official documentation :
componentDidMount() is invoked immediately after a component is mounted (inserted into the tree)
it does mean that first time your render method is unable to read the title of your product (first time that your render method is invoked, this.state.product is still an empty array). I suggest you to check if your array is empty
render() {
if (this.state.product) {
return (
<Text>Loading...</Text>
} else
return (
<View><Text>{this.state.product.title}</Text></View>
)
}
Don't use componentWillMount() because these methods are considered legacy and you should avoid them in new code.
componentWillMount()
If your render function actually does look like you posted, then this can't work. Try chaning your render function to something like this.
render() {
const { product } = this.state
if (!product || !product.title) {
return null
}
return (
<View><Textstyle={styles.name}>product.title</Text></View>
)
}

How to extend bookshelf.js

I have a validate function that I am copying into almost every model. I want to abstract it by extending the base bookshelf.Model object. I'm not sure what the correct way to go about this is in ES6. I'd like to do this without forking bookshelf.
An example model:
import bookshelf from '../bookshelf';
import Checkit from 'checkit';
const Design = bookshelf.Model.extend({
tableName: 'foo',
constructor: function() {
bookshelf.Model.apply(this, arguments); // super()
this.on('saving', this.validate.bind(this));
},
validations: {
barColumn: ['required', 'integer', 'greaterThan:0'],
},
validate: function(model, attrs, options) {
let validations;
if (options.patch === true) {
Object.keys(this.validations).forEach((value, index) => {
if (this.attributes[index] !== undefined) {
validations[index] = value;
}
});
} else {
validations = this.validations;
}
return new Checkit(validations).run(this.toJSON());
}
});
export default Design;
The main bookshelf file is here.
I couldn't work out how to extend Bookshelf so I solved it like this:
import bookshelf from '../bookshelf';
import validate from '../utils/validate';
const Design = bookshelf.Model.extend({
tableName: 'foo',
constructor: function() {
bookshelf.Model.apply(this, arguments); // super()
this.on('saving', validate);
},
validations: {
barColumn: ['required', 'integer', 'greaterThan:0'],
},
});
export default Design;
And the new validate file:
import Checkit from 'checkit';
export default function validate(model, attributes, options) {
let validations;
if (options.patch === true) {
Object.keys(model.validations).forEach((value, index) => {
if (attributes[index] !== undefined) {
validations[index] = value;
}
});
} else {
validations = model.validations;
}
return new Checkit(validations).run(model.toJSON());
};
I think you need Facade or Decorator pattern + some dependency injection.
I used Facade for my own purposes the following way:
class MyFacade {
constructor(legacyLibrary) {
this.legacyLibrary = legacyLibrary;
}
newMethod() {
this.legacyLibrary.doSomething();
this.legacyLibrary.doSomethingElse();
}
}
export default MyFacade;
Idea of decorator is the same, but you should extend your existing class. So all the functionality will remain the same, but you'll have more methods in it.
The good thing about decorator is that you can nest them. Decorate with one, then with another, etc.