I'm writing an SPA in Svelte. Now, I'm fairly new to the concepts of ES6 so I'm having difficulties wrapping my head around some basic concepts.
I have a store:
import { writable } from "svelte/store";
function selectedOptions() {
const selection = writable([
{
id: 1,
title: "Title 1",
selections: []
},
{
id: 2,
title: "Title 2",
selections: []
},
{
id: 3,
title: "Title 3",
selections: []
},
{
id: 4,
title: "Title 4",
selections: []
},
{
id: 5,
title: "Title 5",
selections: []
},
{
id: 6,
title: "Title 6",
selections: []
}
]);
return {
subscribe: selection.subscribe,
updateSelection: item => {
selection.update((items) => {
//I want to update the object with the same id as the object
//I'm passing in to the method.
});
};
}
}
export default selectedOptions();
In my component, I want to pass an object and update the corresponding object in my array with provided values:
function handleChange(e) {
selectedOptions.updateSelection({
id: 1, title: "Title 1", selections: ["Option 1, Option 2"]
});
}
How do I "replace" an existing object with a new one thus triggering an update to all components that are subscribing to the store?
Use the spread syntax to copy all the original keys, then add the one you want to modify:
selection.update(items => {
return {
...items,
[item.id]: item
}
});
You could use the array method map and merge the new and old object if the id matches or just return the old object as-is if the id doesn't match.
updateSelection: item => {
selection.update(items => {
return items.map(i => (i.id === item.id ? { ...i, ...item } : i));
});
};
Related
I have a simple JSON object
simple_chart_config = {
chart: {
container: "#tree-simple"
},
nodeStructure: {
text: { name: "Question" },
children: [
{
text: { name: "Answer 1" }
}
]
}
};
And I'd like to add a new subfield within the first entry on the children array so that the final output is
simple_chart_config = {
chart: {
container: "#tree-simple"
},
nodeStructure: {
text: { name: "Question" },
children: [
{
text: { name: "Answer 1" },
children: [
{
text: { name: "Question 2" }
}
]
}
]
}
};
I've tried several methods, such as
var questionTwoStr = '{"children": [{"text": { "name": "Question 2" }}]}'
var questionTwo = JSON.parse(questionTwoStr);
simple_chart_config.nodeStructure.children[0] = questionTwo;
but I'm having issues working out all of the nested indexes in my head. This is for a tree in treant.js if that context is helpful at all.
I think I'm mostly confused because the place I'm trying to add the new subfield doesn't have a key, which I thought was required for JSON.
There's no need to use JSON here; you can add the object itself:
simple_chart_config.nodeStructure.children[0].children = [{text: { name:"Question 2" }}];
I want to append objects in my array using setState function.
My initial variable looks like this:
columns1: [
{
title: "Sr. No.",
render(text, record, index) {
return {
children: <div> {index + 1}</div>,
};
},
},
{
title: "Account ID",
dataIndex: "Accountid",
},
{
title: "Alias",
dataIndex: "Alias",
},
],
Now, in a function I want to add 2 more objects in columns1:
const projCol = {
title: "Projected Cost",
dataIndex: "forecast_amount",
key: "forecast_amount",
},
const projChange = {
title: "Percentage Change",
dataIndex: "%change",
key: "%change",
}
if I only wanted to add projCol I can do
this.setState((prevState) => {
return {
columns1: [...prevState.columns1,projCol],
};
});
but how can I add both projCol and projChange?
Just add one more element to the array like you do with projCol, separated by commas.
this.setState((prevState) => {
return {
columns1: [...prevState.columns1, projCol, projChange]
};
});
I am completely new to typescript, and I'm stumped by this error message: Property 'name' does not exist on type 'Employee[]' Could someone please point out where I'm not applying the "name" type within the Employee array? Thanks.
interface Employee {
id: number;
name: string;
title: string;
}
var employees: Employee[] = [
{ id: 0, name: "Franklin", title: "Software Enginner" },
{ id: 1, name: "Jamie", title: "Human Resources" },
{ id: 2, name: "Henry", title: "Application Designer" },
{ id: 3, name: "Lauren" title: "Software Enginner" },
{ id: 4, name: "Daniel" title: "Software Enginner 2" },
];
function fetchEmployeeName(id : number) {
var employee = employees.filter(
(employee) => employee.id === id
);
// The error occurs when I try to return a "name" under an employee that matched by id.
return employee.name;
}
console.log("Got Employee: "), fetchEmployeeName(3));
filter returns a new array containing all matching items:
[1, 2, 3].filter(i => i === 4)
The above will return an empty array.
What you want to use is find, which will return a single matching item or undefined.
Modify the fetchEmployeeName function to use find:
function fetchEmployeeName(id : number): string | null {
var employee = employees.find(
(employee) => employee.id === id
);
if (employee === undefined) return null;
return employee.name;
}
Try using find instead of filter. Filter returns an array. Find returns a single object. Next time, if using vscode, hover over employee on the first line of fetchEmployeeName, and check its type. Intellisense in vscode will point out to you that employee is clearly an array.
I highly recommend you to use find instead of filter, but if you really want to stick to your approach, you will have to access the only member in the employees array though its index (filter returns an array filled with the elements that meet the specified condition). E.G.:
return employee[0].name
Again, you can solve this particular issue by using filter, since it returns a single element you access without the need of an index (this will allow you to leave the return statement as it is).
there you have it, so what happened, your filter is returning to a new "Employee" that is not defined as an object,my advise is to always try to use pure functions and understand what your return is
interface Employee {
id: number;
name: string;
title: string;
}
var employees: Employee[] = [
{ id: 0, name: "Franklin", title: "Software Enginner" },
{ id: 1, name: "Jamie", title: "Human Resources" },
{ id: 2, name: "Henry", title: "Application Designer" },
{ id: 3, name: "Lauren", title: "Software Enginner" },
{ id: 4, name: "Daniel", title: "Software Enginner 2" },
];
function fetchEmployeeName (id:number, employees: Employee[]){
let employee = null
for (let i = 0, j = employees.length ; i < j; i++) {
if (employees[i].id === id) {
employee = employees[i].name
}
}
return employee
}
console.log(`Got employee 3: ${fetchEmployeeName(3,employees)}`);
in my react app, there is a setting "page" with different setting categories with respective setting items.
Hence, I need a nested state (?) to avoid having lots of properties called "category1-id", "category1-element1-id" etc.
That looks as follows:
this.state = {
categories: [
{
id: 1,
label: "Category 1",
elements: [
{ id: 1, label: "Option 1", selected: true },
{ id: 2, label: "Option 2", selected: false },
],
},
{
id: 2,
label: "Category 2",
elements: [
{ id: 1, label: "Option 1", selected: true },
{ id: 2, label: "Option 2", selected: false },
{ id: 3, label: "Option 3", selected: false },
],
},
Other property: "...",
To update the state, I need a deep copy instead of a shallow one. How do I achieve this? Is there any other alternative solution?
Using
let categories = [...this.state.categories];
categories = JSON.parse(JSON.stringify(categories));
doesn't work (error) and is not really fail-proof.
Since this is the only nested state in my app, I'd like to avoid using lodash & co.
The function called when clicking on the option looks like this. But this modifies the state directly.
handleOptionSelect = (categoryId, option) => {
const categories = [...this.state.categories];
// FIND INDEX OF CATEGORY
const category = categories.find((category) => category.id === categoryId);
const indexCat = categories.indexOf(category);
// //FIND INDEX OF ELEMENT
const elements = [...category.elements];
const indexEle = elements.indexOf(element);
//MODIFY COPIED STATE
const e = categories[indexCat].elements[indexEle];
e.selected = e.selected ? false : true;
// //SET NEW STATE
this.setState({ categories });
}
Thank you very much for your help! I am still at the beginning trying to learn React and JS :)
What about if you use an object to store your categories? I mean, use a hash of key values in which each key is the id of the category.
this.state = {
categories: {
1: {
label: "Category 1",
elements: [
{ id: 1, label: "Option 1", selected: true },
{ id: 2, label: "Option 2", selected: false },
],
},
2: {...}
}
}
This should help you add or update any category, for example:
// Update category of id 4
this.setState({...categories, 4: updatedCategory })
Same principle should apply to the elements of the category if you use an object to store them.
Creating a container component by connect() function with defined mapStateToProps gives access to state (store.getState()) of entire tree state.
For example, I have combined state tree:
{
loaded: true,
threads: [
{
id: 1,
title: "Thread 1",
messages: [
{
id: 1,
text: "Message 1"
}
]
},
{
id: 1,
title: "Thread 2",
messages: [
{
id: 2,
text: "Message 2"
},
{
id: 3,
text: "Message 3"
}
]
}
]
}
How should I access particulary messages of given thread?
const mapStateToProps = (state) => {
return {
messages: getMessagesByThread(state.threads, <threadId>)
}
}
In this case lets assume that I don't want to have 'activeThreadId' param in store state tree (store.getState().activeThreadId).
What is the best other possibility to provide threadId?
If you have threadId as a prop on your component, you could pass it over via the ownProps param:
const mapStateToProps = (state, ownProps) => {
return {
messages: getMessagesByThread(state.threads, ownProps.threadId)
}
}
This is the best way to go I think.