route.params not visible inside useFocusEffect() - react-navigation

With React Navigation 5.x, params are passing back to parent function. Here is the code in parent receiving 2 params index and type:
import { useFocusEffect } from "#react-navigation/native"
export default MyWork = ({navigation, route}) => {
console.log("route.params :", route.params); //<<==route.params : {"index": 0, "type": "forsale"}}
....
useFocusEffect(
useCallback(() => {
console.log("in navigation", route.params); //<<== route.params undefined
try {
if (route.params?.index && route.params?.type) {
updateCnt(route.params.type, route.params.index);
}
} catch(err) {
}
}, []));
However inside the useFocusEffect, route.params becomes undefined. What is wrong here?

It looks like you need to add the route dependency to useCallback, otherwise it uses the old value (in this case undefined) rather than the updated one.
useFocusEffect(
useCallback(() => {
console.log('in navigation', route.params);
try {
if (route.params?.index && route.params?.type) {
updateCnt(route.params.type, route.params.index);
}
} catch (err) {
}
}, [route]),
);
A similar question was answered here where you can find more details https://stackoverflow.com/a/64190936/13912074

Related

Slack Bolt App: Options body view state is not updated like actions body view state

I am trying to implement dependent external selects inside a modal but I am having problems passing the state of the first dropdown to the second. I can see the state I need inside the app.action listener but I am not getting the same state inside the app.options listener.
body.view.state inside app.action("case_types"). I specifically need the case_create_case_type_block state.
"state": {
"values": {
"case_create_user_select_block": {
"case_create_selected_user": {
"type": "users_select",
"selected_user": "U01R3AE65GE"
}
},
"case_create_case_type_block": {
"case_types": {
"type": "external_select",
"selected_option": {
"text": { "type": "plain_text", "text": "Incident", "emoji": true },
"value": "Incident"
}
}
},
"case_create_case_subtype_block": {
"case_subtypes": { "type": "external_select", "selected_option": null }
},
"case_create_case_owner_block": {
"case_owners": { "type": "external_select", "selected_option": null }
},
"case_create_subject_block": {
"case_create_case_subject": {
"type": "plain_text_input",
"value": null
}
},
"case_create_description_block": {
"case_create_case_description": {
"type": "plain_text_input",
"value": null
}
}
}
},
body.view.state inside app.options("case_subtypes")
"state": {
"values": {
"case_create_user_select_block": {
"case_create_selected_user": {
"type": "users_select",
"selected_user": "U01R3AE65GE"
}
}
}
},
I did also try to update the view myself hoping it would update the state variables inside app.action({ action_id: "case_types" })
//need to update view with new values
try {
// Call views.update with the built-in client
const result = await client.views.update({
// Pass the view_id
view_id: body.view.id,
// Pass the current hash to avoid race conditions
hash: body.view.hash,
});
console.log("Case Type View Update result:");
console.log(JSON.stringify(result));
//await ack();
} catch (error) {
console.error(error);
//await ack();
}
I ended up posting this on the github issues page for slack bolt. This was a bug that will fixed in a future release. Below is the workaround using private metadata to hold the state to check for future dependent dropdowns.
// Action handler for case type
app.action('case_type_action_id', async ({ ack, body, client }) => {
try {
// Create a copy of the modal view template and update the private metadata
// with the selected case type from the first external select
const viewTemplate = JSON.parse(JSON.stringify(modalViewTemplate))
viewTemplate.private_metadata = JSON.stringify({
case_type: body.view.state.values['case_type_block_id']['case_type_action_id'].selected_option.value,
});
// Call views.update with the built-in client
const result = await client.views.update({
// Pass the view_id
view_id: body.view.id,
// Pass the current hash to avoid race conditions
hash: body.view.hash,
// Pass the updated view
view: viewTemplate,
});
console.log(JSON.stringify(result, 0, 2));
} catch (error) {
console.error(error);
}
await ack();
});
// Options handler for case subtype
app.options('case_subtype_action_id', async ({ body, options, ack }) => {
try {
// Get the private metadata that stores the selected case type
const privateMetadata = JSON.parse(body.view.private_metadata);
// Continue to render the case subtype options based on the case type
// ...
} catch (error) {
console.error(error);
}
});
See the full explaination here: https://github.com/slackapi/bolt-js/issues/1146

What is the best way to handle Http Exception error messages in vuejs?

I'm working on a vue SPA project as front-end and Laravel as my back-end. I realized that on every component methods I keep repeating the same code over and over. I would like more advice on how to make a global class that will handle some of the the Http Exception error messages that have been return from the back-end server with or without customized messages Instead of checking it on every method DRY code.
Here is what I have been doing so far:
catch(error => {
if (error.response.status === 422) {
this.serverValidation.setMessages(error.response.data.errors);
} elseif (error.response.status === 403) {
this.error_403 = error.response.data.errors;
} elseif(error.response.status === 402) {
this.error_402 = error.response.data.errors;
}
....
});
I usually make a js/mixins/Errors.js file with all my error methods and then just include the mixin in my vue files.
export const errorsMixin = {
methods: {
errorClass(item, sm = false) {
return { 'form-control': true, 'form-control-sm': sm, 'is-invalid': this.validationErrors.hasOwnProperty(item) }
},
buttonClass() {
return ['btn', 'btn-primary', this.saving ? 'disabled' : '']
},
handleError(error) {
this.saving = false
if (error.response.status == 422) {
this.validationErrors = error.response.data.errors;
} else {
alert(error.response.data.message)
}
},
}
}
In each vue file:
import { errorsMixin } from '../../mixins/Errors.js'
export default {
mixins: [errorsMixin],
......
.catch((err) => {
this.handleError(err)
})
}

d3-drag - “Cannot read property 'button' of null”

I am using the neo4j-d3 charts in my application as follows. I did checkout the git repo of Neo4j-d3 and created seperate repo as 'neo4jd3'
import { Component } from 'react'
import { Redirect } from 'react-router-dom';
import Neo4jd3 from 'neo4jd3';
import * as d3 from 'd3';
class xxx extends Component {
constructor(props) {
super(props);
this.getAllValues = this.getAllValues.bind(this);
this.nodeRadius = 30
}
componentWillMount() {
this.getAllValues ();
}
createGraphObj(data) {
let nodeRadius = this.nodeRadius
let neo4jd3Obj = new Neo4jd3('#graphId', {
nodeLetters: {
'Test': (node) => {
return 'Check'
},
'Check': function (node) {
return 'Check'
}
},
nodeColors: {
'TestColor': 'rgba(250,204,163,1)',
'CheckColor': 'rgba(124,212,248,1)',
},
nodeOutlineColors: {
'TestColor': 'rgba(245,151,67,1)',
'CheckColor': 'rgba(18,147,212,1)',
},
infoPanel: false,
minCollision: nodeRadius,
neo4jData: data,
nodeRadius: nodeRadius,
nodeletterSize: 16,
onNodeClick: (node) => {
}
})
}
getAllValues () {
this.xxx.getAllValues().then(res => {
// console.log('----- getAllDomains -----', res)
let temp = {
"results": [{
"data": [{
"graph": res[0]
}]
}]
}
this.createGraphObj(temp);
})
}
render() {
return (
<div>
<div>
<div id="graphId"></div>
</div>
</div>
)
}
}
export default xxx
. Grpah wise all are fine, but at the time of dragging I am getting the following error
Cannot read property 'button' of null
from the following code
function defaultFilter() {
return !d3Selection.event.button;
}
I saw the following link
d3-drag 0.3.0 - "Cannot read property 'button' of null"
but no luck :(
Help me out for this one.
Thanks in advance

Error TypeError: Cannot read property 'dispatch' of undefined at app.js:12012

Hi I've been trying to learn vuejs and vuex while trying to get response from an api call with vuex concept I got the following error.Please help.
This error occurred
Error TypeError: Cannot read property 'dispatch' of undefined
at app.js:12012
loginAction.js
export const getUsersList = function (store) {
let url = '/Apis/allUsers';
Vue.http.get(url).then((response) => {
store.dispatch('GET_USER_RES', response.data);
if (response.status == 200) {
}
}).catch((response) => {
console.log('Error', response)
})
}
loginStore.js
const state = {
userResponse: []
}
const mutations = {
GET_USER_RES (state, userResponse) {
state.userResponse = userResponse;
}
}
export default {
state, mutations
}
login.vue
import {getUsersList} from './loginAction';
export default {
created () {
try{
getUsersList();
}catch(e){
console.log(e);
}
},
vuex: {
getters: {
getUsersList: state => state.userResponse
},
actions: {
getUsersList
}
}
}
</ script>
If you call the actions manually (like in your try/catch) they'll not get the store context as the first argument. You could use getUsersList(this.store) I think, but instead I would use dispatch to reach all your actions. (I edited just a little bit to get a minimal running example, but I think you get the point!)
new Vue({
render: h => h(App),
created() {
this.$store.dispatch('getUsersList');
},
store: new Vuex.Store({
getters: {
getUsersList: state => state.userResponse
},
actions: {
getUsersList
}
})
}).$mount("#app");
Also, use commit to reach the mutations instead of dispatch. ie:
export const getUsersList = function ({commit}) {
let url = '/Apis/allUsers';
Vue.http.get(url).then((response) => {
commit('GET_USER_RES', response.data); // because GET_USER_RES is a mutation
...

Only reloading resolves without reloading html

How can i force ui router to reload the resolves on my state without reloading the entire ui/controller since
I am using components and since the data is binded from the state resolve,
i would like to change some parameters (pagination for example) without forcing the entire ui to reload but just the resolves
resolve : {
data: ['MailingListService', '$transition$', function (MailingListService, $transition$) {
var params = $transition$.params();
var ml = params.id;
return MailingListService.getUsers(ml, params.start, params.count)
.then(function (result) {
return {
users: result.data,
totalCount: result.totalCount
}
})
}],
node: ['lists', '$transition$', function (lists, $transition$) {
return _.find(lists, {id: Number($transition$.params().id)})
}]
},
I would like to change $transition$.params.{start|count} and have the resolve updated without reloading the html.
What you requested is not possible out of the box. Resolves are only resolved, when the state is entered.
But: one way of refreshing data could be, to check for state parameter changes in $doCheck and bind them to the components by hand.
Solution 1
This could look something like this:
export class MyComponent {
constructor($stateParams, MailingListService) {
this.$stateParams = $stateParams;
this.MailingListService = MailingListService;
this.paramStart = null;
this.paramCount = null;
this.paramId = null;
this.data = {};
}
$doCheck() {
if(this.paramStart !== this.$stateParams.start ||
this.paramCount !== this.$stateParams.count ||
this.paramId !== this.$stateParams.id) {
this.paramStart = this.$stateParams.start;
this.paramCount = this.$stateParams.count;
this.paramId = this.$stateParams.id;
this.MailingListService.getUsers(this.paramId, this.paramStart, this.paramCount)
.then((result) => {
this.data = {
users: result.data,
totalCount: result.totalCount
}
})
}
}
}
Then you have no binding in the parent component anymore, because it "resolves" the data by itself, and you have to bind them to the child components by hand IF you insert them in the template of the parent component like:
<my-component>
<my-child data="$ctrl.data"></my-child>
</my-component>
If you load the children via views, you are obviously not be able to bind the data this way. There is a little trick, but it's kinda hacky.
Solution 2
At first, resolve an empty object:
resolve : {
data: () => {
return {
value: undefined
};
}
}
Now, assign a binding to all your components like:
bindings: {
data: '<'
}
Following the code example from above, where you resolve the data in $doCheck, the data assignment would look like this:
export class MyComponent {
[..]
$doCheck() {
if(this.paramStart !== this.$stateParams.start ||
this.paramCount !== this.$stateParams.count ||
this.paramId !== this.$stateParams.id) {
[..]
this.MailingListService.getUsers(this.paramId, this.paramStart, this.paramCount)
.then((result) => {
this.data.value = {
users: result.data,
totalCount: result.totalCount
}
})
}
}
}
And last, you check for changes in the child components like:
export class MyChild {
constructor() {
this.dataValue = undefined;
}
$doCheck() {
if(this.dataValue !== this.data.value) {
this.dataValue = this.data.value;
}
}
}
In your child template, you access the data with:
{{ $ctrl.dataValue | json }}
I hope, I made my self clear with this hack. Remember: this is a bit off the concept of UI-Router, but works.
NOTE: Remember to declare the parameters as dynamic, so changes do not trigger the state to reload:
params: {
start: {
dynamic: true
},
page: {
dynamic: true
},
id: {
dynamic: true
}
}

Resources