I have been trying to figure this out for awhile but basically I have a list of tags with tags with onClick properties that should change a state variable postsShown to true. Tricky thing is the postsShown is supposed to be an array of boolean values and uses the index to differentiate which tag was clicked. Now the state variable does update but gets stuck and does not get detected by my conditional statement.
this is my code, I have tried calling the setState function with a callback but get an error
Your onClick event is not changing the object just the content inside the object. Since the pointer to the object didn't change React will not re-redner your component.
change your onClick function:
onClick={()=>{
const postsClone = [...postsShown];
postsClone[index]= true;
setPostsShown(postsClone);
}}
[...postsShown] will create a new reference by copying the top level of the object.
If you need to create a deep copy use
const postsClone = JSON.parse(JSON.stringify(postsShown));
Related
I am using Antd Tree to render the tree view of the data, at the beginning the data of the tree is a flat array of objects with each object having a key parentId indicating the parent of that node.
I am then using a function to convert this flat array into hierarchical structure to pass it to the Tree component of the antd library
Please refer this Codesandbox link to see the project code
the flat array is in the data.json file in the src folder
I have done this because pasting the entire code of the project would look clumsy. There is a textbox to enter the new nodes name after selecting an existing node in the tree as the parent of the new node, there are no errors and in the render method in the console the updated state is also getting logged correctly but the tree is not getting rendered reflecting the updated data, I am using shouldComponentUpdate method thinking it would update the DOM if anyone could resolve the problem then it will be a great help
THANK YOU
The thing is in the handleAddNode function you update the flatTree from the component's state, but based on the code below:
<Tree
treeData={this.state.hirarchicalTree}
onSelect={this.handleOnSelect}
/>
, the treeData prop is given from hirarchicalTree.
The flatTree is up to date, but the hirarchicalTree no, so you need to update it from the flat tree.
The shouldComponentUpdate function doesn't help you, so you can remove it.
You have two options to update the **hirarchicalTree:
Using a setState callback function
Adding componentDidUpdate, and do an update if flatTree is different than before
Basically update the hirarchicalTree from the flatTree in the way you first set it in componentDidMount
After this you'll have another problem at this block:
[...flat].forEach(function(item) {
all[item.id] = item;
});
As item is an Object, when you assign it, all[item.id] will point to the same reference as item from the flat variable.
So when you'll add children, they will be added also on the items from the flatTree, rendering duplicates in the page. You can see this answer.
You can fix it by doing this:
[...flat].forEach(function(item) {
all[item.id] = { ...item };
});
This will make a shallow copy, but as we don't have nested fields in item, it's fine.
Working codesandbox here
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
I have created the following demo to help me describe my question: https://codesandbox.io/s/dazzling-https-6ztj2
I have a form where I submit information and store it in a database. On another page, I retrieve this data, and set the checked property for the checkbox accordingly. This part works, in the demo this is represented by the dataFromAPI variable.
Now, the problem is that when I'd like to update the checkboxes, I get all sorts of errors and I don't know how to solve this. The ultimate goal is that I modify the form (either uncheck a checked box or vice versa) and send it off to the database - essentially this is an UPDATE operation, but again that's not visible in the demo.
Any suggestions?
Also note that I have simplified the demo, in the real app I'm working on I have multiple form elements and multiple values in the state.
I recommend you to work with an array of all the id's or whatever you want it to be your list's keys and "map" on the array like here https://reactjs.org/docs/lists-and-keys.html.
It also helps you to control each checkbox element as an item.
Neither your add or delete will work as it is.
Array.push returns the length of the new array, not the new array.
Array.splice returns a new array of the deleted items. And it mutates the original which you shouldn't do. We'll use filter instead.
Change your state setter to this:
// Since we are using the updater form of setState now, we need to persist the event.
e.persist();
setQuestion(prev => ({
...prev,
[e.target.name]: prev.topics.includes(e.target.value)
// Return false to remove the part of the array we don't want anymore
? prev.topics.filter((value) => value != e.target.value)
// Create a new array instead of mutating state
: [...prev.topics, e.target.value]
}));
As regard your example in the codesandbox you can get the expected result using the following snippet
//the idea here is if it exists then remove it otherwise add it to the array.
const handleChange = e => {
let x = data.topics.includes(e.target.value) ? data.topics.filter(item => item !== e.target.value): [...data.topics, e.target.value]
setQuestion({topics:x})
};
So you can get the idea and implement it in your actual application.
I noticed the problem with your code was that you changed the nature of question stored in state which makes it difficult to get the attribute topics when next react re-renders Also you were directly mutating the state. its best to alway use functional array manipulating methods are free from side effects like map, filter and reduce where possible.
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.
I want to pass the appropriate makeData function into the data element in ReactTable according to which year is selected. I keep getting a syntax error and I'm not sure why it won't let me compile it as is.
My logic is this:
The select value has an onChange function that will call handleChange whenever a different year is selected. Once handleChange is called, it will set the value to the event target.
The data object will then pass to the "data" element in ReactTable as "this.value"
There's a lot of stuff wrong here:
Functional components don't have state. Use a class that extends React.Component if you need state
handleChange can't be placed inside of the JSX. Again, use a class and make that a method of the class
this.state.value has the function in a string. You probably want this.state = {value: makeData2014()};. You do this in your JSX as well. Wrap the function call in curly braces instead of strings like so: <option value={makeData2014()}>2014</option>
When you call setState, you're updating this.state.value, not this.value. Thus, you don't need this.value at all and you should remove it.
To top it off, you didn't add the specific error message, so I can't even be sure this is all of it.
Step one should be fixing up all of these issues. After that, if you still have any problems, update your question with the new code and the specific error message you're getting.