Access elements of an Observable Array - reactjs

I have a prop named blockers that returns an observable array of objects. These objects contain 'userId.'
How can I go about returning an array that contains these userIds?

Computed observables can help with situations like these. Make sure that your component is decorated as a mobx-react #observer, that way it will automatically make props observable.
#computed get userIds () {
return this.props.blockers.map(blocker => blocker.userId)
}

Related

How to iterate through an array of objects and return one that has a specific object ID using JSX (in mapStateToProps of React-Redux)

I am trying to pass an object (called a lecture) to the props from state using mapStateToProps. The object exists in an array of objects. I believe I am making a syntax error but am uncertain on exactly what is the correct syntax should be because the code below works for cycling through a parent object (that contains objects inside of it) and select the correct object inside of the parent.
//This code works to cycle through a parent Object that contains objects inside of it; returning the object inside that has the correct ID.
//It doesn't work in this situation ... where instead of having objects inside a parent object,
//there are obejcts inside a parent Array.
const mapStateToProps = (state) => {
const lectureID = state.selectedLectureID;
console.log(lectureID); <--- THIS SHOWS THE CORRECT ID OF THE OBJECT I AM TRYING TO READ.
const lectures = state.firestore.ordered.lectures;
console.log(lectures); <------ THIS SHOWS THE ARRAY OF OBJECTS, INCLUDING ONE OBJECT THAT HAS THE LECTURE ID FOUND ABOVE
const selectedLecture= lectures ? lectures[lectureID] : null; <--- ERROR HERE... TRYING TO CYCLE THROUGH THE ARRAY OF LECTURES AND PASS THE LECTURE THAT HAS THE CORRECT LECTURE ID. IF NONE HAVE THIS ID, THEN IT SHOULD RETURN "null".
console.log(selectedLecture); <--- THIS RETURNS "undefined", SUGGESTING THE CODE ABOVE DIDN'T READ THROUGH THE ARRAY OF OBJECTS AND SELECT THE ONE WITH THE CORRECT ID.
return {
selectedLecture: selectedLecture,
}
}
NOTE: The code above appears interpret the lectureID as an index in the array rather than an attempt to identify the objects ID within the array of Objects. How can I correct this?
I FOUND AN ALTERNATIVE SOLUTION:
Instead of trying to iterate across the array of objects inside mapStateToProps, I found it easier to simply pass the object with the specific ID as an object through Actions + Reducers, and interacting with the content via the state.

How to modify state of a parent component from a child component which is a custom form I wrote. This might also effect other children of the parent

I wrote a custom form component called SingleFilterContainer
Within the parent component called FiltersContainer, I initialize a state filters which has an array of a single filter initially and respective setFilters function to modify the array of filters. And also has a button to add a filter. And in the render function I use filters.map to render SingleFilterContainer component multiple times. So far this works. But I want to add a delete filter button. I put this inside the SingleFilterContainer. When I click this it should update the state filters in the parent component and delete the ith filter and render the rest of the filters normally. Or render the whole map of filters again.
I'm new to react and hooks, and I feel like I'm missing something. I'm at it for the past three days and I'm a little bit lost.
Here is the sandbox https://codesandbox.io/s/editing-filters-j2qrp
I feel like how I'm handling state is completely wrong. And maybe I should use redux? But I want the SingleFilterContainer to be like an ephemeral form. Or should the delete filter button be within the parent component? and repeat it using map?
TL;DR Fixed fork
The problem in your code is that you call the function on render onClick={handleDeleteFilter(i)} the onClick expects a reference to a function, but if you want this code to work, then the method that you are passing from the parent to the child needs to return a function also.
Then your handleDeleteFilter will look like this:
function handleDeleteFilter(i) {
return function() {
filters.splice(i, i + 1);
setFilters(filters => filters);
}
}
Also in your case you don't need to pass i + 1 into splice as the second argument, as the second argument is the amount of items to remove. Which in your case is just 1. Docs
You pass the i to the first function and the second one will see it due to closure.
And then to the removing of an element.
Mutating the state is bad practice, so you can have a local variable with a copy of the state, which you can manipulate and then update the state. That way you can use whatever you want, but on the local variable.
So your updated handleDeleteFilter would look like
function handleDeleteFilter(i) {
return function() {
const clone = [...filters]
clone.splice(i, 1);
setFilters(clone);
}
}
or with .filter
function handleDeleteFilter(i) {
return function() {
const clone = filters.filter((item, index) => index !== i)
setFilters(clone);
}
}
this way you don't need a new variable as .filter returns a new array. Docs
Here's my fork.
The following is my changes.
function handleDeleteFilter(i) {
setFilters(filters => filters.filter((e, index) => index !== i));
}
Important notes:
Ideally, you need to assign an id property for each item in the array instead of comparing by index.
Don't directly mutate the state. .splice directly mutates the state. Learn to use .map, .filter & .reduce array functions

Passing Array as Props in vue Modal

I want to pass array from one component to the other in vueJs, which i am able to do with
<add-new-admin-modal
:permissions = "permissions"
</add-new-admin-modal>
In my other component which is a modal actually,
I am receiving the props as,
props: {
permissions: {
type: Array,
default: () => []
}
}
Here when i try to change the permissions array, it reflects the parent data, As mentioned in the documentation.
https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow
so i tried with spread operator
data () {
return {
statePermissions: [...this.permissions]
}
}
The statePermissions array is still empty when i try above method,
I even tried with Object.assign
data () {
return {
statePermissions: Object.assign([], this.permissions)
}
}
Still it doesn't work.
In my Modal, I am accessing it as
<div v-for = "permission in statePermissions" :key = "permission.id">
...someHtml here.
</div>
The main idea is, I have a component which gets the data through an api, then i have a modal which takes this data and updates it accordingly and submit it to an api.
when the modal is closed, the parent component should need to have the unedited data, so that if modal is reopened it should get unedited data.
In the process of using Modal, My parent component remains in the same state (It neither gets mounted nor changed), so their is no point in making the request for default data again from parent.
Your problem is probably that the default value for you prop is an empty array and you're assigning it to a local variable before the property is properly populated by the parent (or it might even be a lifecycle issue).
Try moving your assignment to the local variable to the mounted() hook or even better if you wan't it to be reactive watch your property:
watch: {
permissions(newValue) {
this.statePermissions = newValue
}
}
You also don't need to ...spread an array to assign it to an array.
Since permissions is an array of objects, when you make a copy of it, the resulting array is a shallow copy meaning it will contain the same references to objects. That's why modifying the new array's values update the old array's values as well. You need to create copies of the objects inside permissions.
If permissions only contains primitives, you can do something like this:
this.statePermissions = JSON.parse(JSON.stringify(this.permissions));
If permissions is of a certain type (i.e. new Permission()), you can map it (I think this is cleaner):
this.statePermissions = this.permissions.map(x => new Permission(x.propA, x.propB, etc.));
This way, each cloned object in statePermissions will have the same properties as the object it's copied from in permissions, but it's independent so modifications won't affect the parent it was created from.
There's a few other ways in this post.

How to check if object is in Mobx observable array?

I'm using indexOf in a React component to style a button based on whether an object is in an mobx observable array.
The button is for favoriting. It pushes the object for that specific list item into an observable array in the store called 'favorites'. favorites is an observable array of objects.
Here's the ES6 template literal from my button:
className={`btn-group ${((this.props.store.favorites.indexOf(this.props.data) > -1)) ? 'success' : 'info'}`}
Basically, it's a check for if the object is in the array, className will be success, if false info.
This works perfectly fine when the favorites array is in local state. But I get that the objects within the favorites array look differently once they are in the observable array. I get that the observable array favorites is different than a local array favorites.
But how do I do a test for whether an object is in an observable array of objects? I tried slice() and peek() and using the findIndex but no dice.
Regarding the doc: isObservableArray
Returns true if the given object is an array that was made observable using mobx.observable(array).
So to know if data is in an observable favorites array:
// If data is a primitive
className={`btn-group ${mobx.isObservableArray(this.props.store.favorites) && this.props.store.favorites.indexOf(this.props.data) > -1 ? 'success' : 'info'}`}
// Id data is an object it is a little more verbose and coupled to your data
// structure. You have to use the `find` function to iterate and test if an
// element in the array has the same id.
className={`btn-group ${mobx.isObservableArray(this.props.store.favorites) && !!this.props.store.favorites.find(fav => fav.id === this.props.data.id) ? 'success' : 'info'}`}
Here is a POC with a function helper: https://jsbin.com/botijom/edit?js,console
Michel (mobx creator) gave me the hint I needed via the Gitter channel.
I actually needed a shallowly observable array, not a deeply observable one. I don't need each and every property of the objects in the array to be observable (hence all the sets/gets on the object properties I was seeing before), just whether an object is added or removed.
So I changed it from
#observable favorites = []
to
#observable favorites = observable.shallowArray();
Ultimately, though #dagatsoin 's answer is right if you need to use a deeply observable array.

How do I update/delete objects inside this.state array in React

So I have a React component where I have:
getInitialState: function () {
return {
peopleDetails: []
}
}
peopleDetails will contain objects which will have property names like name, email, phone etc.
Here's the problem: I'll be passing methods to child components which should mutate the objects of this array. Some methods will just edit a person's email, another will edit the phone etc. Thus, they'll need to call setState.
As far as I understand, setState should return a new copy of the object. I find this impossible to do because things are difficult enough even if I have to mutate the object (I'll have to specify some filters to find it inside an array, then change its value etc).
I'm pretty sure a lot of React apps use data structures like these...where in the state you have an array holding objects and you'll need to do stuff to those objects. Is there some easy way to accomplish this, respecting the "rule" that each setState invocation should return a completely new state object?
If you really don't want to use immutable data, then you can directly mutate the properties you like and invoke a this.forceUpdate() instead of setState after mutations have occurred.
If you were to proceed with immutable data, then adding to that array would return a new array containing the new object. If you removed that person, you'd then return a new empty array. If you were to modify an item in-place in the array, then you'd build a new array using concat/slice to get all elements that haven't changed, then add your changed element, then add everything after the changed element's index.
For a more in-depth discussion on avoiding array mutations, check out this talk.

Resources