React detect variable change across many files - javascript

I have a global variable on my reducer (Redux) code which is a array of objects that contains the data.
I'm constantly passing that variable across multiple files (view files) using connect from React Redux like this:
function mapStateToProps(state) {
const { appointments } = state.Appointment;
return {
appointments
};
}
export default connect(mapStateToProps, {
/* Some methods */
})(Appointment);
When I want to modify a specific element of that array, I pass the reference of that element so I later won't have to deal with replacements. The problem is that someone (a view) is modifying that reference and it's changing it from an Object to a Number type. Is their a easy way such a PropTypes (in the reducer file) to detect who is changing the reference of that array position from an Object to a Number?
Thanks

Related

Vuex: store.state.activities has a key "list" that is an array of 3 items, however store.state.activities.list returns an empty array

My project is in Vue.
store.state.activities is an object that has 2 keys, one of them is an array called list that has 3 items.
However, when I try to reference it using store.state.activities.list, I get an empty array.
I've tried making both a shallow and a deep copy of store.state.activities, in both cases the copy has an empty list array.
store.state.activities structure:
{
"list": [
{
// some data
},
{
// some data
},
{
// some data
}
],
"dictionaries": {}
}
console.log(store.state.activities) - you can see list has 3 items:
whereas store.state.activities.list returns an empty array:
the usual reasons for this are you've not initialised or accessed the vuex correctly
initialising
is usually done in your main file and should look something like
import { store } from "../store";
createApp(App)
.use(store)
.mount("#app");
accessing
if you are using the Options API you have to access the store via the this in the components, which looks as so
this.$store.state.activities
if you are using the composition API however the this object is not available in which case you should do something like
import {useStore} from "vuex"
...
setup(){
const store = useStore();
return {
activities : computed(()=>store.state.activities),
...
if you are just importing the store object you have defined then you will be getting a uninitialised version that wont be picking up any changes you make
if using type script the code is a little different
the final posibility i can think of is your assignment to the list property
if you are doing
this.$store.state.activities.list = [{},{}.{}];
then you will be replacing the list which will break the reference, assignment should be done via a mutator as vuex will wrap these changes in reactive wrapers that tell watchers of changes
mutations: {
setList(state, value) {
state.activities.list = value;
},
...
},
which would then be called as
store.commit("setList", [{},{},{}]);

State Mutation detected when updating Redux Values

So I'm fairly new to React-Redux and I'm facing this problem where if i update this array with a new object in redux, i'm getting the following error
Uncaught Invariant Violation: A state mutation was detected between dispatches, in the path `settingsObject.arrayForSettings.0.updatedObject`. This may cause incorrect behavior.
The Problem only arises when i update the state in redux with new values, On previous values there is no error. The piece of code that is giving me the error is as follows.
let tempArrayForSettings = [...props.arrayForSettings];
tempArrayForSettings.forEach((element:any) => {
if(element.settingsType === "DesiredSettingType")
{
//modify elements of a JSON object and add it to the element
element.updatedObject = JSONUpdatedObject;
}
//call the action method to update the redux state
updateAction(tempArrayForSettings);
});
The error is pointing me to the following link : Redux Error
I know I'm not updating the object I'm getting from props instead I'm making a copy using the spread operator so I really don't know why the error is coming whenever I'm firing the updateAction function.
Well, your line element.updatedObject = JSONUpdatedObject; is modifying the object in the Redux store. element is a direct reference to your store object. You would need to do an immutable copy here - your spread above only does a shallow copy, but the items here are still the same.
Generally, you should do logic like this not in your component, but within your Reducer. (see the Style Guide on this topic) That also gives you the benefit that you don't need to care about immutability, as within createSlice reducers you can simply modify data.
You're updating the object inside the array, so the spread operator that you've created above only clones the array itself, not the objects inside the array. In Javascript, objects are passed by reference (read more here)
To fix this warning, you'd need to copy the object itself, and to do that, you'd need to find the specific object in the array that you'd like to change.
Try this:
const { arrayForSettings } = props;
const modifiedSettings = arrayForSettings.map((element:any) => {
if(element.settingsType === "DesiredSettingType")
{
//modify elements of a JSON object and add it to the element
return {
...element,
updatedObject: JSONUpdatedObject,
}
}
return element;
}
updateAction(modifiedSettings);
});
Also, it's recommended that this logic lives on the reducer side, not in your component.

How to have data shared between Vue3 single file component instances?

I don't need to pass data between a parent component to a child one or the opposite, I need something like php/c static variables.
I want my sfc (single file component) to have some data that is shared among all instances in in the page.
As far as I understand that's why in sfc we define data as a function
export default {
data(){
return {
// props here
};
}
}
while in page scripts we can define it as an object
const app = new Vue({
data: {
// props here
},
}
That's because since we can have multiple instances of a sfc in the page defining its data as a function make each instance to execute in and get its own data, while with page script we can have a singe instance.
I need to define some of my sfc data to be shared between component instances, while other data to be per-instance.
Is there a way to do this?
That depends on the data to be defined, its complexity, and purpose.
If these are 2 or 3 readonly variables, they can be set as global properties using Vue.prototype (Vue 2) or app.config.globalProperties (Vue 3). I'm not sure, because in your example you use Vue 2 syntax.
If the data should be reactive, you can set up a simple state management as explained in the Vue documentation: Simple state management.
If the data is more complex than that, the next step will be Vuex.
Following #Igor answer I looked after the simple state management and found the ref() method that creates reactive primitive values.
In my specific use case I needed to share among all the sfc instances just an array, so in my sfc I had:
const reactive_array = ref([]);
export default {
data() {
return {
shared_array: reactive_array,
};
},
};

Angular 2 Component Factory Resolver Helper Function

I have been working with the Component Factory Resolver for awhile and while I think it's pretty slick, there is one thing that drives me nuts. I would love to wrap most of the repeated code into a helper function that renders the component.
In my case I have a dashboard component where we render quite a few different components by altering singleton services to trigger visibility or not. Rather than having a ton of these create component code blocks, I was wondering if anyone has successfully create a helper-like function that a few variables can be passed into to achieve the same effect, thus eliminating a lot of the repetitive code.
Below is my attempt at a helper function and the call to activate it. The component gets created, but the destroy function doesn't work. I have narrowed it down to the Component Reference not actually being saved to the globally accessible component. Is there a way to store component references within a global array? If so how would you go about dynamically accessing them as components are added/destroyed?
Subscription within ngOnInit
// Subscribe to Create User Modal Visibility
this._ComponentVisibilityService.createUserVisibility$.subscribe(
_createUserVisibility => {
this.renderComponent(
this.createUserModal,
CreateUserComponent,
this.createUserModalContainer,
_createUserVisibility
)
}
)
Function within the dashboard component
renderComponent(component, template, container, visibility) {
if (visibility) {
// Destroy previously built components if not already destroyed
if (component) component.destroy();
// Generate component factory
const componentFactory = this._ComponentFactoryResolver.resolveComponentFactory(template);
// Render the component
component = container.createComponent(componentFactory);
} else {
// Destroy the component if visibility is false
if (component) component.destroy()
}
}
So ended up doing some digging last night and found that Typescript considers any to be a basic (primitive) type. So from what I can tell, none of the methods, or structure are available to component, unless altered to not fit into the "basic" category, otherwise its passed as a value and not a reference. However, like in javascript, an object is not considered a primitive type so I refactored the component variable to be cast as an object with a property component as ComponentRef<any>, and it worked!
Component Property Declaration
createUserModal: { component: ComponentRef<any> } = { component: null }
Function Declaration
renderModal(component: { component: ComponentRef<any> }, template, container, visibility) {
// Create Component...
}
This combination allows the object to passed in as a reference which in turn allows the function to alter the value of DashboardComponent property this.createUserModal, which in turn allows all of the methods to be called on it as normal.

Adding functions to redux store within reducer is an anti pattern?

say I have a component connected to the redux store. Within this store, there's a list of objects. For instance something like this:
ReduxStore: {
dataList: [
{name:'bla'},
{name:'blub'},
]
}
Is it actually an anti pattern to create and add a filter function within the reducer to create something like this:
ReduxStore: {
dataList: {
data:[
{name:'bla'},
{name:'blub'}
],
isNameAvailable: (name) => {/* search for name */}
}
}
It works great, but I'm not sure whether this was the intended way to go.
It's an anti-pattern because your store only cares about data, not computation. Once you start adding functions to your store, you lose the ability to serialize the data inside it.
However, it's pretty trivial to pull these kinds of helper functions out and turn them into standalone utility selectors which you can use to achieve the same thing.
function isNameAvailable(store, name) {
/* search for `name` in `store` */
}
Now the function works independently of the store and you can keep them separate for testing.
From here, you can take a look at Reselect which allows you to turn your isNameAvailable function into a cached selector, meaning you'll only need to re-calculate as and when the appropriate data in the store is changed.
From Redux documentation:
It’s very important that the reducer stays pure. Things you should never do inside a reducer:
Mutate its arguments;
Perform side effects like API calls and routing transitions;
Call non-pure functions, e.g. Date.now() or Math.random().
so I think if you want to declare a filter function isNameAvailable, in my opinion, you have two options:
use the connect function to filter
// the solution of connect
import React, { Component } from 'react';
import { connect } from 'react-redux';
const App = () => (
<div>the example</div>
);
function isNameAvailable(state) {
// your code
}
function mapStateToProps(state) {
return isNameAvailable(state)
}
module.exports = connect(mapStateToProps)(App);
declare a filter function in action
In my experience,I think the first one is better. I hope this can help you.

Categories

Resources