Async action creator not dispatching action as expected - ecmascript-6

guys.
I have the following async action creator that dispatches another action creator or returns null depending on the current state. The problem is that it is invariably returning null no matter what the current state is.
// actions.js
export const loadPosts = category => (dispatch, getState) => {
const currentState = getState()
const posts = currentState.postsByCategory
const categoryPosts = posts[category]
const items = categoryPosts.items
if(items === []) {
return dispatch(fetchPosts(category))
}
return null
}
As you can see, the action creator dispatching fetchPosts() depends on the value of itemsbeing equal to an empty array. I am testing it providing it an intial state with the following structure:
// initialState
const initialState = {
postsByCategory: {
hot: {
isFetching: false,
items: []
}
}
}
I am obviously not accessing the items property appropriately, but I cannot see where the error in my code is.
I am testing this with redux-mock-store, creating an instance of a mockStore, and providing it with initialState.
Hope you guys can pinpoint the error in my code.
Thanks in advance.

Your problem lies in the comparison
if(items === [])
In the above code, items will not be === to [] as they both are of different instances. If you want to check if items is empty, please use items.length === 0. Hope that helps.

Related

Typescript conditional code is running regardless of confirm() value

I am trying to implement a dialogue box that confirms with the user if they want to continue with an action.
deleteItem(index: number): void {
let response = window.confirm("Are you sure?");
if (response) {
// delete item code
this.item.splice(index,1);
console.log("A");
} else {
console.log("B");
}
}
When I click the deleteItem button to trigger the dialogue, it deletes the item regardless but prints the correct console.log() string. Can anyone help me understand why this is happening and how to correct this?
Are you sure? "Ok" => deletes the item and logs "A" in console.
Are you sure? "Cancel" => deletes the item and logs "B" in console.
Unaltered code:
deleteItem(index: number): void {
let response = window.confirm("Are you sure?");
if (response) {
// delete feedback in students' boolean feedback arrays
for (var i = 0; i < this.csvRecords.length; i++) {
if (this.students[i].feedbackBoolean[index] == true) {
// add deduction value to student grade before delete
var newGrade = parseFloat(this.students[i].grade) + this.feedback[index].deduction
this.students[i].grade = newGrade.toString();
}
this.students[i].feedbackBoolean.splice(index,1);
}
// remove 1 element at index
this.feedback.splice(index,1);
console.log("A");
} else {
console.log("B");
}
}
I added the unaltered code to see if there is anything that could elicit this behavior that I might be overlooking. Thanks in advance.
Please forgive me for using javascript to implement your example, but I found that it seems that there is no problem. Can you provide a reproducible example?
const items = [0, 1, 2, 3, 4];
const deleteItem = (index) => {
let response = window.confirm("Are you sure?");
if (response) {
// delete item code
items.splice(index, 1);
console.log("A");
} else {
console.log("B");
}
}
deleteItem(0);
console.log(items);

useSelector function is not updating the state of after dispatch function -react hooks

I am performing an authentication module where I when I click the sign in button , I am verifying user present is MySQL db or not . I am dispatching the function in here in sign in page
Basically when I dispatch it , the null state of the rSignedIn is not changed immediately after dispatch function. I am completely using react hooks. Please help me solve this , I have been trying this for three days.
But the rSignedIn state value updates when I click the login button again, in general , the when I use the state value using the useSelector the value is updated the second the time when the handleLogin() is invoked
//Sign in Page
...
...
const status=useSelector((state)=>state);
...
...
const handleLogin=(event)=>{
dispatch(LoginUser(loginData));
console.log(status.auth.rSignedIn);
if(status.auth.rSignedIn){
console.log("LOGIN success");
History.push('/');
}else{
console.log("LoginFailed") ;
}
}
this is the action index page where I sent a request to MySQL db , then if there is a response I am dispatching it else an error.
export const LoginUser=(loginData)=>async(dispatch)=>{
await mysqlDB.post('/fetch/retreive',loginData)
.then((response)=>dispatch({type:ActionTypes.LOGIN_SUCCESS,payload:response.data}))
.catch((error)=>dispatch({type:ActionTypes.LOGIN_FAILED}))
}
This is my Reducer for this :
const initialState = {
gSignedIn:null,
userId:null,
registered:null,
data:null,
rSignedIn:null,
}
export default (state=initialState,action)=>{
switch (action.type){
case ActionTypes.GSIGN_IN:
return {...state,gSignedIn:true,userId: action.payload};
case ActionTypes.GSIGN_OUT:
return {...state,gSignedIn:false,userId:null};
case ActionTypes.REGISTER_SUCCESS:
return {...state,registered:true,data: action.payload};
case ActionTypes.REGISTER_FAILED:
return {...state,registered:false,data:null};
case ActionTypes.LOGIN_SUCCESS:
return {...state,rSignedIn:true,data: action.payload};
case ActionTypes.LOGIN_FAILED:
return {...state,rSignedIn:false,data:null};
case ActionTypes.LOGOUT:
return {...state,rSignedIn:false,data:null};
default:
return state;
}
};
dispatch will not update your state value immediately. State value is bound by closure and will only update in your next render cycle.
You can either use history.push within your action or make use of useEffect
const handleLogin=(event)=>{
dispatch(LoginUser(loginData, History));
}
...
export const LoginUser=(loginData, history)=>async(dispatch)=>{
await mysqlDB.post('/fetch/retreive',loginData)
.then((response)=>{
dispatch({type:ActionTypes.LOGIN_SUCCESS,payload:response.data}));
history.push('/')
}
.catch((error)=>{
dispatch({type:ActionTypes.LOGIN_FAILED}))
}
}
With the useEffect, you need to run the it only on change and not on initial render
const initialRender = useRef(true);
useEffect(() => {
if(!initialRender.current) {
if(state.auth.rSignedIn) {
history.push('/');
} else {
console.log(not signed in);
}
} else {
initialRender.current = false;
}
}, [state.auth.rSignedIn])

Too tidious hooks when querying in REST. Any ideas?

I've just started using feathers to build REST server. I need your help for querying tips. Document says
When used via REST URLs all query values are strings. Depending on the service the values in params.query might have to be converted to the right type in a before hook. (https://docs.feathersjs.com/api/databases/querying.html)
, which puzzles me. find({query: {value: 1} }) does mean value === "1" not value === 1 ? Here is example client side code which puzzles me:
const feathers = require('#feathersjs/feathers')
const fetch = require('node-fetch')
const restCli = require('#feathersjs/rest-client')
const rest = restCli('http://localhost:8888')
const app = feathers().configure(rest.fetch(fetch))
async function main () {
const Items = app.service('myitems')
await Items.create( {name:'one', value:1} )
//works fine. returns [ { name: 'one', value: 1, id: 0 } ]
console.log(await Items.find({query:{ name:"one" }}))
//wow! no data returned. []
console.log(await Items.find({query:{ value:1 }})) // []
}
main()
Server side code is here:
const express = require('#feathersjs/express')
const feathers = require('#feathersjs/feathers')
const memory = require('feathers-memory')
const app = express(feathers())
.configure(express.rest())
.use(express.json())
.use(express.errorHandler())
.use('myitems', memory())
app.listen(8888)
.on('listening',()=>console.log('listen on 8888'))
I've made hooks, which works all fine but it is too tidious and I think I missed something. Any ideas?
Hook code:
app.service('myitems').hooks({
before: { find: async (context) => {
const value = context.params.query.value
if (value) context.params.query.value = parseInt(value)
return context
}
}
})
This behaviour depends on the database and ORM you are using. Some that have a schema (like feathers-mongoose, feathers-sequelize and feathers-knex), will convert values like that automatically.
Feathers itself does not know about your data format and most adapters (like the feathers-memory you are using here) do a strict comparison so they will have to be converted. The usual way to deal with this is to create some reusable hooks (instead of one for each field) like this:
const queryToNumber = (...fields) => {
return context => {
const { params: { query = {} } } = context;
fields.forEach(field => {
const value = query[field];
if(value) {
query[field] = parseInt(value, 10)
}
});
}
}
app.service('myitems').hooks({
before: {
find: [
queryToNumber('age', 'value')
]
}
});
Or using something like JSON schema e.g. through the validateSchema common hook.

basic reducer possibly mutating app state

I am using Redux spread operator to hopefully mantain the state as immutable objects.
However, i am managing to make the most simple unit test fail.
I assume the error probably has to do with immutables, but am i not using the spread operator correctly?
Here is my unit test:
describe('app logic', () => {
it('initialises app', () => {
const newState = reducer(INITIAL_STATE, {type: "NEXT"})
const expectState = {
start: true,
render_component: null,
requests: {},
results: {},
}
console.log('newState', newState)
console.log('expected state', expectState)
expect(newState).to.equal(expectState)
})
})
and here is my reducer
export const INITIAL_STATE = {
start: false,
render_component: null,
requests: {},
results: {}
}
export const next = (state) => {
if (state === INITIAL_STATE) {
return {
...state,
start: true,
}
}
return state
}
export function reducer(state = INITIAL_STATE, action) {
switch (action.type) {
case 'NEXT':
return next(state)
default:
return state
}
}
I print the two objects, and they look the same.
i get the error :
1) app logic initialises app:
AssertionError: expected { Object (start, render_component, ...) } to equal { Object (start, render_component, ...) }
Not sure exactly which testing library you are using, but usually a name like .equal is used to test strict equality ( === ), which means (at least in the case of objects) that the two things being compared must actually reference the exact same object. So, for example,
const original = { a: 1 }; // creates a new object, assign it
const testMe = { a: 1 }; // creates another new object, assign it
console.log( original === testMe ) // false
evaluates to false, because while the objects have the same content, they do not reference the exact same object. They are separate, independently created, objects that happen to have the same content. Compare that to
const original = {a: 1}; // create a new object
const testMe = original; // create another reference to the same object
console.log( original === testMe ); // true
So when you return
return {
...state,
start: true,
}
you are creating and returning a new object, so it naturally can not reference the same object that you created and assigned to the variable name expectedState.
If what you are interested in is not strict equality, but rather just that the content in the two objects are the same, there exists other methods than .equal, usually named something with deep (since they go deep into the objects/arrays/whatever to check if the values are the same).
Chai.js has examples of both expect(x).to.equal(y) and expect(x).to.deep.equal(y) in their docs: http://chaijs.com/api/bdd/#method_equal
Your testing library probably has very similar, if not identical, syntax.

LocalStorage: get every value from each key

I want to go through every key and get the name value from each key.
This is how my LocalStorage looks like.
key: 3 Value:
{"name":"Kevin","country":"Canada","about":"Test","image":""}
key: 4 Value:
{"name":"Homer","country":"Canada","about":"Test","image":""}
I want to getboth of these names and add them to my array. I tried it with this method:
for(var key in localStorage){
let user = JSON.parse(localStorage.getItem(key));
this.users.push(user);
}
Error I get is:
SyntaxError: Unexpected token e in JSON at position 1
var keys = Object.keys(localStorage);
keys.forEach(key=>{
var json_str =localStorage.getItem(key)
try {
var abc = JSON.parse(json_str);
this.user = abc;
} catch (e) {
console.log(e)
}
})
when you say I want to getboth of these names, i don't get it but either way you can try something like:
var keys = Object.keys(localStorage);
for(var i=0;i<keys.length;i++){
var key = keys[i];
console.log(key, localStorage[key]);
//store here "both names" where you want them
//you can also access each element with localStorage[key].name, localStorage[key].country, etc.
}
This is a refinement of Robert's answer.
Just enumerate all of the values (The keys themselves do not matter) in localStorage that have a name property that is a string. Then return that array.
Based on your own answer, you likely have inconsistent mutable state as moving your temporary variable to instance scope should not impact your situation.
function getUsers() {
return Object.values(localStorage)
.map(json => {
try {
return JSON.parse(json);
}
catch (e) {
return undefined;
}
})
.filter((user?: any): user is {name: string} => user && typeof user.name === 'string');
}
const users = getUsers();
You can consider using my library ngx-store to deal with localStorage, sessionStorage, cookies and a bit more in Angular. To achieve what you want you will be able to store whole array in storage or just use code like the below with your current data structure:
import { LocalStorageService } from 'ngx-store';
export class Example {
public users: Array<any> = [];
constructor(public localStorageService: LocalStorageService) {
this.localStorageService.utility.forEach((value, key) => this.users.push(value));
}
}
Really, it can be just that simple ;)
You can use method hasOwnProperty('propertyName') to check name available or not in object. Then perform the operation that you want.
let localStorage = {
"key1": {"name":"Kevin","country":"Canada","about":"Test","image":""},
"key2": {"name":"Homer","country":"Canada","about":"Test","image":""}
}
for(let key of Object.keys(localStorage)){
if(localStorage[key].hasOwnProperty('name')){
console.log(localStorage[key]['name']);
}
}
const allItems = []
const keys = Object.keys(window.localStorage); // all keys
keys.forEach(key=> {
const item = JSON.parse(localStorage.getItem(key) + ''); //item with type Object
allItems.push(item);
});
console.log(allItems) // arry of object
I manage to solve it, was a simple error by always initializing a new let user inside the loop.
I moved out the user and the rest of the code works.
user: any;
getUsers():void{
for(var key in localStorage){
this.user = JSON.parse(localStorage.getItem(key));
this.users.push(this.user);
}
}