ReactJS with typescript - javascript

i creating a form with React-selec.
When i do a function and pass parameters he returns me
Expected 1 arguments, but got 0.ts(2554)
index.tsx(31, 31): An argument for 'selectRef' was not provided.
useEffect(() => {
function parseSelectValue(selectRef: { state: { value: any } }) {
const selectValue = selectRef.state.value
if (!multiple) {
return selectValue ? selectValue.id : ''
}
return selectValue
? selectValue.map((option: { id: any }) => option.id)
: []
}
registerField({
name: fieldName,
ref: ref.current as any,
path: 'state.value',
parseValue: parseSelectValue,
clearValue: (selectRef: { select: { clearValue: () => void } }) => {
selectRef.select.clearValue()
}
})
parseSelectValue()
}, [fieldName, registerField, multiple])

The function parseSelectValue expects a selectRef; Its a non-optional parameter.
function parseSelectValue(selectRef?: { state: { value: any } }) {
const selectValue = selectRef ? selectRef.state.value : undefined;
Paste this should fix the problem.

Related

Compare nested objects and list out differences in JavaScript

I want the difference in such a way that the I don't return the entire nested object if any of the values is different.
I have seen solutions online and they all return the entire nested objects and it doesn't work if only 1 key-value pair is changed. i don't want to show the difference as a complete nested object. it should be easier for any user to read.
for eg:
const A = {
position: 2,
attributes: [{
code: 123,
name: "xyz",
params: {
label: "hehe",
units: "currency"
}
}],
code: 1
}
const B = {
position: 3,
attributes: [{
code: 123,
name: "xyzr",
params: {
label: "heh",
units: "currency"
}
}],
code: 1
}
I want the output to be like this:
difference: {
position: {
current: 2,
previous: 3
},
attributes: {
current : [{ name: "xyz", params: { label: "hehe" } }],
previous: [{ name: "xyzr", params: {label: "heh"}}]
}
}
The code that I tried:
const compareEditedChanges = (A: any, B: any) => {
const allKeys = _.union(_.keys(A), _.keys(B));
try {
setDifference(
_.reduce(
allKeys,
(result: any, key) => {
if (!_.isEqual(A?.[key], B?.[key])) {
result[key] = {
current: A[key],
previous: B[key]
};
}
return result;
},
{}
)
);
} catch (err) {
console.error(err);
}
return difference;
};
After giving it a lot of thought to the code, I came with my own solution for a deeply nested objects comparison and listing out the differences in an object with keys as current and previous.
I didn't use any inbuilt libraries and wrote the code with simple for loop, recursion and map
const compareEditedChanges = (
previousState,
currentState
) => {
const result = [];
for (const key in currentState) {
// if value is string or number or boolean
if (
typeof currentState[key] === 'string' ||
typeof currentState[key] === 'number' ||
typeof currentState[key] === 'boolean'
) {
if (String(currentState[key]) !== String(previousState[key])) {
result.push({
[key]: {
current: currentState[key],
previous: previousState[key]
}
});
}
}
// if an array
if (
Array.isArray(currentState[key]) ||
Array.isArray(previousState[key])
) {
console.log(currentState[key])
if (currentState[key].length > 0 || previousState[key].length > 0) {
currentState[key].map((value, index) => {
// check for array of string or number or boolean
if (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'boolean'
) {
if (
JSON.stringify(currentState[key]) !==
JSON.stringify(previousState[key])
) {
result.push({
[key]: {
current: currentState[key],
previous: previousState[key]
}
});
}
}
// check for array of objects
if (typeof value === 'object') {
const ans = compare(
value,
previousState[key][index]
);
result.push(ans);
}
});
}
}
}
return result;
};
You first need a object:
const [object, setObject] = useState({
number: 0,
text: "foo"
});
You need to check when the object changed with useEffect, but you also need to see the previos object, for that we will be using a helper function.
const prevObject = usePrevious(object);
const [result, setResult] = useState("");
useEffect(() => {
if (prevObject) {
if (object.number != prevObject.number) {
setResult("number changed");
}
if (object.text != prevObject.text) {
setResult("text changed");
}
}
}, [object]);
//Helper function to get previos
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
Here is the Codesandbox

Javascript: update an object from its method without this keyword

I am working with a library which implement a class Graph that have a method Graph.registerNode()
The methode has the following signature.
Graph.registerNode('node-name', {
object: {
options: {
key: "value"
},
updateOption: () => {
this.options = { key: "other value" }
}
})
Is it possible to update the options key from updateOptions without using the this keyword?
const options = {
key: "value"
};
Graph.registerNode('node-name', {
object: {
options,
}
updateOption: () => {
options.key = "other value";
}
})
How about this?
I was able to solve this via a factory function
const factoryFunction = () => {
let options: {
key: "value"
}
return Object.freeze({
updateOption: () => {
options = {
key: 'updated value',
}
},
printOption: () => {
console.log(options)
}
})
}
const config = factoryFunction()
Graph.registerNode('node-name', config)

Quasar QSelect is not opening when performing AJAX call

I have been trying to create a simple auto complete using Quasar's select but I'm not sure if this is a bug or if I'm doing something wrong.
Problem
Whenever I click the QSelect component, it doesn't show the dropdown where I can pick the options from.
video of the problem
As soon as I click on the QSelect component, I make a request to fetch a list of 50 tags, then I populate the tags to my QSelect but the dropdown doesn't show.
Code
import type { PropType } from "vue";
import { defineComponent, h, ref } from "vue";
import type { TagCodec } from "#/services/api/resources/tags/codec";
import { list } from "#/services/api/resources/tags/actions";
import { QSelect } from "quasar";
export const TagAutoComplete = defineComponent({
name: "TagAutoComplete",
props: {
modelValue: { type: Array as PropType<TagCodec[]> },
},
emits: ["update:modelValue"],
setup(props, context) {
const loading = ref(false);
const tags = ref<TagCodec[]>([]);
// eslint-disable-next-line #typescript-eslint/ban-types
const onFilterTest = (val: string, doneFn: (update: Function) => void) => {
const parameters = val === "" ? {} : { title: val };
doneFn(async () => {
loading.value = true;
const response = await list(parameters);
if (val) {
const needle = val.toLowerCase();
tags.value = response.data.data.filter(
(tag) => tag.title.toLowerCase().indexOf(needle) > -1
);
} else {
tags.value = response.data.data;
}
loading.value = false;
});
};
const onInput = (values: TagCodec[]) => {
context.emit("update:modelValue", values);
};
return function render() {
return h(QSelect, {
modelValue: props.modelValue,
multiple: true,
options: tags.value,
dense: true,
optionLabel: "title",
optionValue: "id",
outlined: true,
useInput: true,
useChips: true,
placeholder: "Start typing to search",
onFilter: onFilterTest,
"onUpdate:modelValue": onInput,
loading: loading.value,
});
};
},
});
What I have tried
I have tried to use the several props that is available for the component but nothing seemed to work.
My understanding is that whenever we want to create an AJAX request using QSelect we should use the onFilter event emitted by QSelect and handle the case from there.
Questions
Is this the way to create a Quasar AJAX Autocomplete? (I have tried to search online but all the answers are in Quasar's forums that are currently returning BAD GATEWAY)
What am I doing wrong that it is not displaying the dropdown as soon as I click on the QSelect?
It seems updateFn may not allow being async. Shift the async action a level up to solve the issue.
const onFilterTest = async (val, update /* abort */) => {
const parameters = val === '' ? {} : { title: val };
loading.value = true;
const response = await list(parameters);
let list = response.data.data;
if (val) {
const needle = val.toLowerCase();
list = response.data.data.filter((x) => x.title.toLowerCase()
.includes(needle));
}
update(() => {
tags.value = list;
loading.value = false;
});
};
I tested it by the following code and mocked values.
// import type { PropType } from 'vue';
import { defineComponent, h, ref } from 'vue';
// import type { TagCodec } from "#/services/api/resources/tags/codec";
// import { list } from "#/services/api/resources/tags/actions";
import { QSelect } from 'quasar';
export const TagAutoComplete = defineComponent({
name: 'TagAutoComplete',
props: {
modelValue: { type: [] },
},
emits: ['update:modelValue'],
setup(props, context) {
const loading = ref(false);
const tags = ref([]);
const onFilterTest = async (val, update /* abort */) => {
// const parameters = val === '' ? {} : { title: val };
loading.value = true;
const response = await new Promise((resolve) => {
setTimeout(() => {
resolve({
data: {
data: [
{
id: 1,
title: 'Vue',
},
{
id: 2,
title: 'Vuex',
},
{
id: 3,
title: 'Nuxt',
},
{
id: 4,
title: 'SSR',
},
],
},
});
}, 3000);
});
let list = response.data.data;
if (val) {
const needle = val.toLowerCase();
list = response.data.data.filter((x) => x.title.toLowerCase()
.includes(needle));
}
update(() => {
tags.value = list;
loading.value = false;
});
};
const onInput = (values) => {
context.emit('update:modelValue', values);
};
return function render() {
return h(QSelect, {
modelValue: props.modelValue,
multiple: true,
options: tags.value,
dense: true,
optionLabel: 'title',
optionValue: 'id',
outlined: true,
useInput: true,
useChips: true,
placeholder: 'Start typing to search',
onFilter: onFilterTest,
'onUpdate:modelValue': onInput,
loading: loading.value,
});
};
},
});

React Redux functional component updating state not working

My data looks like this:
{
'004': [
{
year_week: '2020-W1',
actual_bank_amount: '6500000',
ext_in_rental_income: '',
ext_in_tax_refund: '',
ext_in_dividends_income: ''
},
{
year_week: '2020-W2',
actual_bank_amount: '6500000',
ext_in_rental_income: '',
ext_in_tax_refund: '',
ext_in_dividends_income: ''
}
],
'007': [
{
year_week: '2020-W22',
actual_bank_amount: '65050000',
ext_in_rental_income: '30000',
ext_in_tax_refund: '',
ext_in_dividends_income: ''
}
]
},
I am trying to update say date for year_week '2020-W1' in '004'.
No problem with action and reducer but data is not updated in the list.
Below is my reducer:
case 'UPDATE':
state.planningData[action.payload.currentSite].map((item, index) => {
if (item.year_week === action.payload.data.year_week) {
return Object.assign({}, item, action.payload.data);
}
return item;
});
console.log(state)
return {
loading: true,
planningData: state.planningData,
error: ''
}
What I am doing wrong please. Btw when I do console log or run redux extension I see the updated state.
Below is my action creator:
export const update = (data) =>
(dispatch, getState) => {
console.log("Update action called" + JSON.stringify(data))
const currentSite = getState().sites.currentSite;
dispatch({
type: 'UPDATE',
payload: {
data: data,
currentSite: currentSite
}
});
};
btw I am calling it from a editable cell component on "enter" and blur event below is my code
const save = async e => {
try {
const values = await form.validateFields();
toggleEdit();
dispatch(update({ ...record, ...values }));
} catch (errInfo) {
console.log('Save failed:', errInfo);
}
};
This isn't pretty but it works. You had a bit of nested data in your state and it wasn't being updated properly.
case "UPDATE":
let updatedPlanningData = {};
for (let prop in state.planningData) {
if (prop === action.payload.currentSite) {
updatedPlanningData[action.payload.currentSite] = state.planningData[
action.payload.currentSite
].map((item, index) => {
if (item["year_week"] === action.payload.data.year_week) {
return Object.assign({}, item, action.payload.data);
}
return item;
});
} else {
updatedPlanningData.prop = state.planningData[prop];
}
}
return {
loading: true,
planningData: updatedPlanningData,
error: ""
};
Here is example code in codesandbox
Edit: more compact solution
let updatedPlanningData = {...state.planningData};
updatedPlanningData[action.payload.currentSite].map((item, index) => {
if (item["year_week"] === action.payload.data.year_week) {
return Object.assign(item, action.payload.data);
}
return item;
});

TypeError: Cannot read property 'caption' of undefined

Trying to test if the following function gets called correctly.
Here is the function, using Jest and Enzyme for React Js:
changeCurrentColumn = (e) => {
{
if(e.target.value !== 'default')
{
const id = e.target.value
const name = this.props.defaultData[e.target.value].caption
this.props.setCurrentColumn({ name, id })
}
}
}
Here is the test:
it('changeCurrentColumn function test', () => {
wrapper.setProps({
defaultData:[{
caption:"test"
},],
}),
wrapper.update();
The error is pointing to ({target:'test'})) :
expect(wrapper.instance().changeCurrentColumn({target:'test'})).toBeDefined();
})
What can I add after 'test' in order to read the property of 'caption'?
Thanks.
You can change the type of the defaultData to object something like this.
it('changeCurrentColumn function test', () => {
const event = {
target: {
value: "caption"
}
}
wrapper.setProps({
defaultData:{
caption:"test"
},
}),
wrapper.update();
expect(wrapper.instance().changeCurrentColumn(event)).toBeDefined()
});
or pass value as 0 to get rest from the existing defaultData.
it('changeCurrentColumn function test', () => {
const event = {
target: {
value: 0
}
}
wrapper.setProps({
defaultData:[{
caption:"test"
}],
}),
wrapper.update();
expect(wrapper.instance().changeCurrentColumn(event)).toBeDefined()
});

Categories