How to fix problem with arguments in function? - javascript

I have a function ,that i'm using in multiple components . It collects data from components and stores it in db. My problem is that function has a lot of arguments and some of them might not be used in some components . Take a look :
export default async function addUserLogs(account, service, terminal,isSaved,isSentToGateway,amount ) {
const obj = {
serviceID: service,
terminalID: terminal,
amountName: account,
amount: amount,
isSaved: isSaved,
isSentToGateway: isSentToGateway,
};
const db = await InitDb();
const tx = db.transaction('userLogs', 'readwrite');
const store = tx.objectStore('userLogs');
const index = await store.put(obj);
let params = {};
if (window.localStorage.getItem('params') !== null) {
params = JSON.parse(window.localStorage.getItem('params'));
}
window.localStorage.setItem(
'params',
JSON.stringify({ ...params, userLogIndex: index })
);
}
For example account and service arguments i'm passing to function in on one component, others arguments is not required . In another component I only need to pass amount argument, but I need to specify previous arguments to do not overwrite other values. But there is an error "account, service, terminal,isSaved,isSentToGateway is not defined". Could you please suggest me how to fix that problem

addUserLogs can receive an object with optional keys and merge the last received object with the new one:
export default async function addUserLogs(newParams) {
const lastParams = <get the last params from storage>;
const obj = Object.assign(lastParams, newParams);
...
}
// usage examples
addUserLogs({amountName: 'x', serviceID: 1, terminalID: 2, isSaved: true ,isSentToGateway: false, amount: 10});
addUserLogs({isSentToGateway: false, amount: 10});
addUserLogs({amountName: 'x', serviceID: 1, terminalID: 2});
If you want to be more declarative about addUserLogs signature you can do something link:
export default async function addUserLogs({amountName, serviceID, terminalID, isSaved ,isSentToGateway, amount}) {
const lastParams = <get the last params from storage>;
const newParams = {
amountName: amountName || lastParams.amountName,
serviceID: serviceID || lastParams.serviceID,
terminalID: terminalID || lastParams.terminalID,
isSaved: isSaved === undefined ? lastParams.isSaved : isSaved,
isSentToGateway: isSentToGateway === undefined ? lastParams.isSentToGateway : isSentToGateway,
amount: amount === undefined ? lastParams.amount : amount
};
...
}

In order to get ride of this and make your function more generic, You should provide default arguments to the function so that when you don't pass any the oject creation here
serviceID: service,
terminalID: terminal,
amountName: account,
amount: amount,
isSaved: isSaved,
isSentToGateway: isSentToGateway,
};
will not break. To do that you use the below format
export default async function addUserLogs(account="default", service = "default", terminal = "default value",isSaved = "default value",isSentToGateway = "default value",amount = "default Value" )
provide default only for variables that are not going to be required in other places when called.
Note when you call this function and provide params they will replace the default but when you don't the function will use the default as such your function doesn't break with undefined variables

in that example you can set null for some arguments that is not required and you don't want to use them when callig your function.
you can use some filters for your arguments like so:
export default async function addUserLogs(account, service, terminal,isSaved,isSentToGateway,amount ) {
const obj = {};
if(account) obj.amountName = account;
if(service) obj.serviceID = service;
if(terminal) obj.terminalID = terminal;
if(isSaved !== null) obj.isSaved = isSaved;
if(isSentToGateway !== null) obj.isSentToGateway = isSentToGateway;
if(amount) obj.amount = amount;
const db = await InitDb();
const tx = db.transaction('userLogs', 'readwrite');
const store = tx.objectStore('userLogs');
const index = await store.put(obj);
let params = {};
if (window.localStorage.getItem('params') !== null) {
params = JSON.parse(window.localStorage.getItem('params'));
}
window.localStorage.setItem(
'params',
JSON.stringify({ ...params, userLogIndex: index })
);
} ```

Related

Single function for saving into localStorage?

How can we save and retain localStorage objects rather than creating multiple functions ? The first localStorage object get replaced with the new save. So to avoid that I have created a new function called saveLocalStorageDataTwo which is working. But how can we avoid creating multiple functions for saving data into the localStorage ? Is there any way ? Could some please advise ?
/* localStorage.js */
let localStorageData = {};
let localStorageDataTwo = {};
function saveLocalStorageData ({autoId, quoteId, taskId }) {
localStorageData = {
autoId: autoId,
quoteId: quoteId,
taskId: taskId,
}
return localStorageData
}
function saveLocalStorageDataTwo ({data}){
localStorageDataTwo = {
data : data,
}
return localStorageDataTwo
}
export { saveLocalStorageData, saveLocalStorageDataTwo };
// Saving to localStorage:
let localData = require("../../support/localStorage");
const data = "Some Data 260-255"
const localStorageData = localData.saveLocalStorageData({ autoId });
window.localStorage.setItem('localStorageData ', JSON.stringify(localStorageData ));
You simply don't use any strict params like {autoId, quoteId, taskId} just pass any arbitrary data.
Don't call something saveLocalStorageData if that's actually not what that function does.
Instead:
const LS = {
set(key, data) { localStorage[key] = JSON.stringify(data); },
get(key) { return JSON.parse(localStorage[key]); },
};
// export { LS };
// import { LS } from "./localstorage.js"
// Example saving multiple data in different LS keys
LS.set("one", {autoId: 1, quoteId: 2, taskId: 3});
LS.set("two", {autoId: 7});
// Example saving Object, and later update one property value
const data = {a: 1, b: 2, c: 3};
LS.set("single", data); // Save
LS.set("single", {...LS.get("single"), c: 99999}); // Update
console.log(LS.get("single")); // {a:1, b:2, c:99999}
// Example saving multiple data into a single Array:
LS.set("arr", []); // Save wrapper Array
LS.set("arr", LS.get("arr").concat({a: 1, b: 2}));
LS.set("arr", LS.get("arr").concat({e: 7, f: 9}));
console.log(LS.get("arr")); // [{"a":1,"b":2}, {"e":7,"f":9}]
jsFiddle playground
or in the last example instead of an Array you could have used an Object. It all depends on the needs.

Function arguments, object with values equal to an empty object

I'm currently working on a MongoDB Javascript developer code.
There is something that I can't wrap my mind around:
This method has some funky business going on in the arguments here. What does {filters = null, page = 0, moviesPerPage = 20} = {} mean? What do you achieve by equating it to an empty object?
static async getMovies({
filters = null,
page = 0,
moviesPerPage = 20,
} = {}) {
/// more code
...}
Here is the entire function for more context should you need it:
static async getMovies({
filters = null,
page = 0,
moviesPerPage = 20,
} = {}) {
let queryParams = {}
if (filters) {
if ("text" in filters) {
queryParams = this.textSearchQuery(filters["text"])
} else if ("cast" in filters) {
queryParams = this.castSearchQuery(filters["cast"])
} else if ("genre" in filters) {
queryParams = this.genreSearchQuery(filters["genre"])
}
}
let { query = {}, project = {}, sort = DEFAULT_SORT } = queryParams
let cursor
try {
cursor = await movies
.find(query)
.project(project)
.sort(sort)
} catch (e) {
console.error(`Unable to issue find command, ${e}`)
return { moviesList: [], totalNumMovies: 0 }
}
/**
Ticket: Paging
Before this method returns back to the API, use the "moviesPerPage" and
"page" arguments to decide the movies to display.
Paging can be implemented by using the skip() and limit() cursor methods.
*/
// TODO Ticket: Paging
// Use the cursor to only return the movies that belong on the current page
const displayCursor = cursor.limit(moviesPerPage)
try {
const moviesList = await displayCursor.toArray()
const totalNumMovies = page === 0 ? await movies.countDocuments(query) : 0
return { moviesList, totalNumMovies }
} catch (e) {
console.error(
`Unable to convert cursor to array or problem counting documents, ${e}`,
)
return { moviesList: [], totalNumMovies: 0 }
}
}
This is a default parameter. It prevents the method from throwing an error if the parameter is not provided.
MDN reference
function getMovies({
filters = null,
page = 0,
moviesPerPage = 20,
} = {}) {
console.log('getMovies', filters, page, moviesPerPage)
}
function getMovies2({
filters = null,
page = 0,
moviesPerPage = 20,
}) {
console.log('getMovies2', filters, page, moviesPerPage)
}
getMovies({})
getMovies()
getMovies2({})
getMovies2()
It is default function parameter.
If when there's not any parameter, the {} would be default value of that prop.
This is necessary because if any parameter is not passed, the value becomes undefined.
In this case, an error occurs when if you attempting to get values such as filters and page from the undefined prop. so it seems that was used.

How to avoid calling same function for a prop for a component : ReactJS

Basically i am calling same function multiple times inside the component for a prop which is inside the render function.
How do i avoid calling the same function multiple times for a component required for that specific prop
Below is my code
getProperties = (name, param) => {
switch(name){
case 'duration':
return param.obj1;
case 'time':
return param.obj2;
case 'date':
return param.obj3;
case 'place':
return param.obj4;
}
}
render() {
return (
<SampleComponent
duration={this.getProperties('duration', param1)}
time={this.getProperties('time', param2)}
date={this.getProperties('date', param3)}
place={this.getProperties('place', param4)}
)
}
So instead of having multiple methods to get the required details. How to use single method which does manipulation and return object with transformed data?
Here is one way to clean up the code:
List down the parameters and have them in an array:
const DURATION = "duration";
const TIME = "time";
const DATE = "date";
const PLACE = "place";
const allParamters = [DURATION, TIME, DATE, PLACE]
Prepare two objects, paramMap and keyMap
paramMap is the object to use to for given param.
keyMap is the map that would say which key to use for given param.
const paramMap = {
[DURATION]: param1,
[TIME]: param2,
[DATE]: param3,
[PLACE]: param4
};
const keyMap = {
[DURATION]: "obj1",
[TIME]: "obj2",
[DATE]: "obj3",
[PLACE]: "obj4"
}
Update the getProperties function and prepare all the props in one place as:
// for getProperties(DURATION), it will resolve to `param1.obj1!`
const getProperties = (key) => paramMap[key][keyMap[key]];
// this will prepare the `props` map as:
// { "duration": param1.obj1, ... }
const propsToPass = allParamters.reduce((props, aParam) => {
props[aParam] = this.getProperties(aParam)
return props;
}, {});
Finally, update the JSX:
<SampleComponent {...propsToPass} />
All you need to do is define paramMap correctly where param1, param2, and others are in scope.
Here is the complete snippet:
const DURATION = "duration";
const TIME = "time";
const DATE = "date";
const PLACE = "place";
const allParamters = [DURATION, TIME, DATE, PLACE]
const paramMap = {
[DURATION]: param1,
[TIME]: param2,
[DATE]: param3,
[PLACE]: param4
};
const keyMap = {
[DURATION]: "obj1",
[TIME]: "obj2",
[DATE]: "obj3",
[PLACE]: "obj4"
}
const getProperties = (key) => paramMap[key][keyMap[key]];
const propsToPass = allParamters.reduce((props, aParam) => {
props[aParam] = this.getProperties(aParam)
return props;
}, {});
<SampleComponent {...propsToPass} />
Where do obj1, obj2, obj3, etc come from? Are they props?
You can also spread an object as a component's props if that's easier
ie
const someObj = {
duration: obj1,
time: obj2,
date: obj3,
place: obj4
}
<SampleComponent {...someObj} />
This adds the props duration, time, date, place, etc to your component. Not sure if I have enough context, but maybe this can help
We can do something like this -:
Also added null fail safe check for the pops.
getPropsForSampleComponent = {
duration: obj1 || null,
time: obj2 || null,
date: obj3 || null,
place: obj4 || null
}
render() {
return (
<SampleComponent
{...this.getPropsForSampleComponent}
)
}

undefined object in reactjs application

I have a container that holds search bar inside a form element consisting of two inputs, from and to, and a button.
On my form submit function, I create an OBJECT called query and it equals to:
const query = {
from : this.state.from,
to : this.state.to
};
then I pass this query object as an argument to an action that have created :
this.props.fetchPlaces(query);
In my action.js inside my fetchPlaces I have :
var skypicker = {
flyFrom : query.flyFrom,
flyTo : query.toCity,
};
I want to be able to pass flyFrom and flyTo to an api that later returns flights from flyFrom to flyTo which return results as JSON!
ofcourse i have to parse them to the URl, but at the current state flyFrom and flyTo are undefined, Am I doing this currectly?
You're accessing properties that you haven't set. The names differ between query and skypicker.
Try creating skypicker like this:
var skypicker = {
flyFrom: query.from,
flyTo: query.to
};
It’s query.from and query.to but not query.flyFrom and query.toCity
Check right one below
var skypicker = {
flyFrom : query.from,
flyTo : query.to
};
I generally do this
request body:
let data = {};
data.firstName = this.state.firstName;
data.lastName = this.state.lastName;
data.email = this.state.email;
data.password = this.state.password;
data.sex = this.state.sex;
this.props.registerUser(data);
action call:
export function registerUser(data) {
return dispatch => {
dispatch(registerUserRequest());
return ajax.post(URL_PREFIX + '/auth/register/', data)
.then(res => {
dispatch(registerUserSuccess(res.data))
setTimeout(function(){
dispatch(push('/login'))
}, 2000)
})
.catch(errors => {
dispatch(registerUserError(errors))
})
}
}

Call nested methods on object only if it exists using Ramda

I currently have a user object, which has a currentRoom() method on it, which might sometimes not exist or return null.
If the currentRoom() method returns something, I then need to call a messages() method on that. If nothing is returned from either, I’d like to return a default empty array [].
I’d like to tackle this functionally using Ramda, so it’s neat and reusable. Currently, my (non Ramda) code looks like this:
const user = Users.findOne({ username: params.id })
const room = (user.currentRoom && user.currentRoom() || {})
const messages = (room.messages && room.messages() || [])
Some kind of logic I was thinking was to pass in the list of required methods, along with the default result if nothing comes out of it.
/* getMessages(defaultIfNull, methodsArray, object) */
getMessages([], ['currentRoom', 'messages'], user)
Basically something a bit similar to pathOr, but for methods on objects.
I guess I'd use the List monad like an Option:
const getRoom = user => "currentRoom" in user ? [user.currentRoom()] : [];
const getMessages = room => "messages" in room ? room.messages() : [];
const allTogether = R.compose(R.chain(getMessage), R.chain(getRoom), R.of);
console.log(allTogether(Users.findOne({ username: params.id })));
You can use a point-free solution using just Ramda. First, we define a withDefaults function which will set up currentRoom and messages methods in objects which these methods are undefined.
var withDefaults = R.pipe(
// if don't have `currentRom` add a
// `currentRom` function that returns an empty object
R.unless(
R.hasIn('currentRoom'),
R.assoc('currentRoom',
R.always({}))),
// if don't have `messages` add a
// `messages` function that returns an empty array
R.unless(
R.hasIn('messages'),
R.assoc('messages',
R.always([]))))
This function filters the object setting up methods if needed. Usage example.
var user = withDefaults(getById(id))
Next, define a getter functions to get rooms and messages from objects. invoker is the central piece of this snipet, it returns a function that invokes a method. See http://ramdajs.com/docs/#invoker
var getCurrentRoom = R.invoker(0, 'currentRoom')
var getMessages = R.invoker(0, 'messages')
Above code can be used as following.
var userWithRoom = withDefaults({
currentRoom : function () {
return {
number : '123'
}
}
})
var userWithMessages = withDefaults({
messages : function () {
return [
'get lunch'
]
}
})
var userWithAll = withDefaults({
currentRoom : function () {
return {
number : '123'
}
},
messages : function () {
return [
'get lunch'
]
}
})
var userWithNone = withDefaults({})
All together
var withDefaults = R.pipe(
R.unless(
R.hasIn('currentRoom'),
R.assoc('currentRoom',
R.always({}))),
R.unless(
R.hasIn('messages'),
R.assoc('messages',
R.always([]))))
var getCurrentRoom = R.invoker(0, 'currentRoom')
var getMessages = R.invoker(0, 'messages')
// examples
var userWithRoom = withDefaults({
currentRoom : function () {
return {
number : '123'
}
}
})
var userWithMessages = withDefaults({
messages : function () {
return [
'get lunch'
]
}
})
var userWithAll = withDefaults({
currentRoom : function () {
return {
number : '123'
}
},
messages : function () {
return [
'get lunch'
]
}
})
Now lets test above code using console.log to see if our solution works as expected
console.log(getCurrentRoom(userWithRoom))
console.log(getCurrentRoom(userWithMessages))
console.log(getCurrentRoom(userWithAll))
console.log(getCurrentRoom(userWithNone))
console.log('---')
console.log(getMessages(userWithRoom))
console.log(getMessages(userWithMessages))
console.log(getMessages(userWithAll))
console.log(getMessages(userWithNone))
The output should be:
{ number: '123' }
{}
{ number: '123' }
{}
---
[]
[ 'get lunch' ]
[ 'get lunch' ]
[]

Categories

Resources