I have written a function which is triggered by a dropdown list's onChange event.
Once the function is triggered, it load data from rest API according to the parameter value. However, the array which I have used to store those data is not cleared in the subsequent dropdown change events. As a result, when the user change the dropdown multiple times, the array grows with its previously loaded data.
for example:
In first drop down change => ['A','B','C']
2nd drop down change => ['A','B','C','E','F','G'] instead of ['E','F','G']
My code is as follows:
onDropdownChange = (e) => {
var newArray = [];
// To remove all the elements of newArray
newArray.forEach((e1,idx, arr) => {
newArray.splice(idx,1);
});
console.log(newArray);
const url = 'https://abcdefgh...../' + e + '/readings?today';
newArray = this.state.data.slice();
axios.get(url).then(res => {
var response_arr = res.data.items;
res.data.items.forEach((item, index) => {
newArray.push({ time: res.data.items[index].dateTime, TM: res.data.items[index].value })
})
let sortedArray = newArray.sort((a, b) => new Date(a.time).getTime() - new Date(b.time).getTime());
this.setState({ data: sortedArray });
this.fetchChart();
});
}
fetchChart = () => {
const { data } = this.state;
return <CustomLineChart data={data} />
}
render(){
const {items} = this.state;
var data = [];
const itemList = items.map(item => {
return <tr key={item.dateTime+''+item.value}>
<td style={{whiteSpace: 'nowrap'}}>{item.dateTime}</td>
<td>{item.measure}</td>
<td>{item.value}</td>
</tr>
});
return (
<div>
{this.fetchChart()}
<CustomDropdownList changeLink={this.onDropdownChange.bind(this)} />
</div>
);
}
}
export default Dashboard;
Can anybody assist me to resolve this issue?
You are copying the previous data with
newArray = this.state.data.slice();
You do not need that.. In fact you do not need any of this if you want to create a new array with new options.
You're adding the results to newArray which starts out empty that you tried to clear, then it's set to the existing data with additional data added. Also, avoid using var.
var newArray = [];
// This is already empty
newArray.forEach((e1,idx, arr) => {
newArray.splice(idx,1);
});
// This will always be empty
console.log(newArray);
const url = 'https://abcdefgh...../' + e + '/readings?today';
// Existing data, remove this
newArray = this.state.data.slice();
axios.get(url).then(res => {
var response_arr = res.data.items;
res.data.items.forEach((item, index) => {
// This is where new data is being added to existing
newArray.push({ time: res.data.items[index].dateTime, TM: res.data.items[index].value })
})
Related
I'm having this map function to map through some table data.
const Rows = dataToFilter.children.map((row: any) => {
return row.table_row;
});
console.log("ROWSSS", Rows);
...and as a result i'm getting this complex array of objects:
How can i get the two columns of arrays highlited?
Don't have the data to test, but this could be a possibility
const Rows = dataToFilter.children.map((row: any) => {
const [colOne, colTwo] = row.table_row.cells;
return {
colOne,
colTwo
}
});
You can try this:
const Rows = dataToFilter.children.map((row: any) => {
return [row.table_row.cells[0], row.table_row.cells[1]];
});
console.log("ROWSSS", Rows);
I just came from the interview, I have implemented multiple filter feature in my assignment.
assignment is live here: https://tooth-store.netlify.app/
here is my code where I am filtering with according to the value of filters state.
const filterData = () => {
let data = [...products];
if (byCategory !== 'all') {
data = data.filter((item) => {
return item.category.toLowerCase() === byCategory.toLowerCase();
});
}
if (byRating !== 'all') {
data = data.filter((item) => Math.floor(item.rating.rate) == byRating);
}
if (bySearch != '') {
data = data.filter((item) =>
item.title.toLowerCase().includes(bySearch.toLowerCase())
);
}
return data;
};
Interviewer told me if we will be having a lot of products then going with this approach is not a good idea, so we have to filter at a one go not for all single value of filters.
example: category filter is applied earlier, and now we are changing the rating then it will again filters the category first. so filter it in a one go.
Can anyone explain me in a detail how I have to deal with this, I got blank at that time, but now i am guessing i simply have to check for all filters values with && operator in a single filter
Is there is any other best way?
You can make a common function, where you can check the category against data, here I give you an example in the below code, I make a common function where I get a two param, one is item this is list of items and the second is category user which category against wants data, In the function, I define the some categories array, you can store the categories dynamically in the state and then check if category exists in the categories array, If exists then you can filter the data against the categories list and If you want to see the full example CLICK HERE.
const func = (item, category) => {
let newArr = [];
if (category !== "all") {
const categories = ["electronics", "men's clothing", "jewelery"];
const ratings = [1.9, 2.5, 6.7];
if (categories.includes(category)) {
newArr = item.category.toLowerCase() === category.toLowerCase();
} else if (ratings.includes(category)) {
newArr = Math.floor(item.rating.rate) === category;
} else {
newArr = item.title.toLowerCase().includes(category.toLowerCase());
}
}
return newArr;
};
const filterData = () => {
let data = [...products];
data = data.filter((item) => {
return func(item, byCategory);
});
return data;
};
you can use this method where your filter option will run for only one type at a time just you have to pass a parameter like this
const filterData = (type) => {
let data = [...products];
if (byCategory !== 'all' && type == 'cat') { // call this filterData('cat')
data = data.filter((item) => {
return item.category.toLowerCase() === byCategory.toLowerCase();
});
}
if (byRating !== 'all'&& type == 'rate') { // call this filterData('rate')
data = data.filter((item) => Math.floor(item.rating.rate) == byRating);
}
if (bySearch != ''&& type == 'search') { // call function by filterData('search')
data = data.filter((item) =>
item.title.toLowerCase().includes(bySearch.toLowerCase())
);
}
return data;
};
I'm trying to add data from a single input field to an array which i want to store in localstorage but when i submit input button, the item is stored at first but if i try to store a second item, the array previous item is replaced with the newly typed item data instead of adding to it like i'd expect an array to behave. i don't understand this behaviour. i will really really appreciate a detailed explanation since i'm using react to do this.
This is my code below
input field
import React from "react";
import "./addoption.css";
function AddOption({ validateOption }) {
const handleAddoption = (e) => {
e.preventDefault();
const inputValue = e.target.elements[0].value.trim();
validateOption(inputValue);
e.target.elements[0].value = "";
};
return (
<div className="addoption">
<form onSubmit={handleAddoption}>
<input type="text" name="list" />
<button>Add Option</button>
</form>
</div>
);
}
export default AddOption;
*this is my code to add the input data to the localstorage *
const handleAddoption = (option) => {
if (!option) {
return setErrorhandler("Enter valid value to add item");
} else if (listItems.options.indexOf(option) > -1) {
return setErrorhandler("This option already exists!");
}
const array = localStorage.getItem("Options");
let items = [];
if (array) {
items = JSON.parse(array);
}
let storedArray = JSON.stringify(items.push(option));
localStorage.setItem("options", storedArray);
setListItems({ options: items });
};
``
Array.prototype.push certainly mutates the array, but it's return value isn't the array, it's the new length of the array. You may want to do the mutation separate from the JSON serializing.
The reason for the overwriting is because you're using two different storage keys for getting and setting. You are not getting what was stored so you are only appending new data to an empty array. Make sure you also use the same key to both retrieve and set the localStorage.
const handleAddoption = (option) => {
if (!option) {
return setErrorhandler("Enter valid value to add item");
} else if (listItems.options.indexOf(option) > -1) {
return setErrorhandler("This option already exists!");
}
const array = localStorage.getItem("options");
let items = [];
if (array) {
items = JSON.parse(array);
}
items.push(option);
localStorage.setItem("options", JSON.stringify(items));
setListItems({ options: items });
};
A more optimal solution would be to read in and initialize the options state from localStorage, and use an useEffect hook to just persist state updates back to localStorage. This way is a little easier to manage.
Example:
const initializeState = () => ({
// ... other listItems initial state
options: JSON.parse(localStorage.getItem("options")) || [],
});
const [listItems, setListItems] = useState(initializeState());
useEffect(() => {
localStorage.setItem("options", JSON.stringify(listItems.options));
}, [listItems.options]);
const handleAddoption = (option) => {
if (!option) {
return setErrorhandler("Enter valid value to add item");
} else if (listItems.options.indexOf(option) > -1) {
return setErrorhandler("This option already exists!");
}
setListItems(prevState => ({
...prevState
options: prevState.options.concat(option),
}));
};
handleSearch = (ev, index) => {
const { dropdown_datas, dropdown_datas_search } = this.state;
const keys = Object.keys(dropdown_datas_search);
const current_key = keys[index];
let value = ev.target.value;
let current_dropdown_data = dropdown_datas_search[current_key];
const filtered_data = current_dropdown_data.filter(robots => {
return robots.label.toLowerCase().includes(value.toLowerCase());
})
dropdown_datas[current_key] = filtered_data
this.setState({
dropdown_datas: dropdown_datas
})
}
I am using this function in react.
"dropdown_datas_search" data is coming from state which data i don't wants change.
But, when i am doing the filter "dropdown_datas_search" is also changing from state.
I wants to only change and filter "current_dropdown_data" data .
Please take a look.
How can i prevent doing this.
I'm trying to return a new array by adding an event listener to my remove button. Connected to that event listener is a function which runs an array.filter() function. I'm not sure why it's not working. I can console.log the new array and that's fine, but when I try to return the new array, nothing happens.
I was splicing the item out of the array and that worked fine, but I wanted to try using filter. It seems that the logic is there and there's no error being thrown. But it just doesn't return a new array. It's the removeIngredient function that I'm trying to remove the item from via its index.
const removeIngredient = text => {
const ingredientIndex = recipeOnPage.ingredients.findIndex(
ingredient => ingredient.text === text
)
const ingredientList = recipeOnPage.ingredients
const ingredient = ingredientList.filter(
item => ingredientList.indexOf(item) !== ingredientIndex
)
return ingredient
}
const toggleIngredient = text => {
const ingredient = recipeOnPage.ingredients.find(
ingredient => ingredient.text === text
)
if (ingredient.included) {
ingredient.included = false
} else {
ingredient.included = true
}
}
const ingredientSummary = recipe => {
let message
const allUnchecked = recipeOnPage.ingredients.every(
ingredient => ingredient.included === false
)
const allChecked = recipeOnPage.ingredients.every(
ingredient => ingredient.included === true
)
if (allUnchecked) {
message = `none`
} else if (allChecked) {
message = `all`
} else {
message = `some`
}
return `You have ${message} of the ingredients for this recipe`
}
const generateIngredientDOM = ingredient => {
const ingredientEl = document.createElement('label')
const containerEl = document.createElement('div')
const checkbox = document.createElement('input')
const ingredientText = document.createElement('span')
const removeButton = document.createElement('button')
recipeStatus.textContent = ingredientSummary(recipeOnPage)
// Setup ingredient container
ingredientEl.classList.add('list-item')
containerEl.classList.add('list-item__container')
ingredientEl.appendChild(containerEl)
// Setup ingredient checkbox
checkbox.setAttribute('type', 'checkbox')
checkbox.checked = ingredient.included
containerEl.appendChild(checkbox)
// Create checkbox button in ingredient div
checkbox.addEventListener('click', () => {
toggleIngredient(ingredient.text)
saveRecipes()
renderIngredients(recipeId)
})
// Setup ingredient text
ingredientText.textContent = ingredient.text
containerEl.appendChild(ingredientText)
// Setup the remove button
removeButton.innerHTML = '<i class="far fa-trash-alt"></i>'
removeButton.classList.add('button', 'button--text')
ingredientEl.appendChild(removeButton)
// Create remove button in ingredient div
removeButton.addEventListener('click', () => {
removeIngredient(ingredient.text)
saveRecipes()
renderIngredients(recipeId)
})
return ingredientEl
}
const renderIngredients = recipeId => {
// Grab the ingredient display from the DOM
const ingredientList = document.querySelector('#ingredients-display')
ingredientList.innerHTML = ''
const recipe = getRecipes().find(item => {
return item.id === recipeId
})
// Iterate through the list of ingredients on the page and render all items from recipeDOM
recipe.ingredients.map(ingredient => {
const ingredientDisplay = generateIngredientDOM(ingredient)
ingredientList.appendChild(ingredientDisplay)
})
saveRecipes()
}
Nothing is happening, as I mentioned above. When I console.log the variable ingredient, I get the new array with the removed item gone, but when I return it, nothing happens.
If I understand the structure correctly, your removeIngredient can probably be trimmed down to:
const removeIngredient = text => {
recipeOnPage.ingredients = recipeOnPage.ingredients.filter(i => i.text !== text);
}
This removes the ingredient which has it's text property same as the text parameter. You are doing of a lot of unnecessary findIndex to get the filtered ingredients and return it. But you're nowhere setting it back to the recipeOnPage object.
Since you aren't using the return value of removeIngredient, you need not return anything