Update field in objects array React - arrays

I have an objects array like this below:
const [categories, setCategory] = useState([
{ text: "a", active: false },
{ text: "b", active: false },
{ text: "c", active: false },
{ text: "d", active: false }
]);
and I want to update the active fields to be true. I tried something like this but it doesn't work correctly at all:
const clearFilters = () => {
setCategory(categories.map((m) => (m.active = true)));
};

const clearFilters = () => {
setCategory(categories.map((category) => ({...category, active: true})));
};
This must work. ".map" s callback function will be executed for every item of array, and every time will expect from you the new changed item. So you must return the new changed item. In my example above I used ES6 destructuring syntax, if you use ES5 it will be like this.
const clearFilters = () => {
setCategory(categories.map((category) => ({text: category.text, active: true})));
};
And yes, if you return the object in arrow function right away instead of executing function, you must put it into parentheses, otherwise it will understand it as function body, not object

Try something like below:-
const clearFilters = () => {
const clearedCategories = categories.map(m => {
m.active = true;
return m;
});
setCategory([...clearedCategories]);
};

Related

How to deleted elements of a list who are meeting a certain condition on React js

I would like to delete all completed items from the to-do list when pressing a button.
How could I do that? My useState is like this :
const [todos, setTodos] = useState([
{ id: 58477, text: "Wash dishes", done: false },
{ id: 64851, text: "Bake a cake", done: true },
{ id: 59858, text: "Make a website", done: true },
])
And my code is like that:
export default function TodoListItem(){
const [ todos, setTodos ] = useTodosContext()
function deleteTodo(todo) {
console.log("Clear completed")
}
return(
<div>
{todos.map(todo => <li><input type="checkbox" className="roundCheckBox"/>{todo.text}</li>)}
<button onClick={() => deleteTodo(todo)}>CLEAR COMPLETED</button>
</div>
)
}
You could use a higher order array method (like filter / forEach) on the todos-state array to segregate the done todos and then remove it from the state , updating state automatically makes react render the todoItems again this time with the new state i.e. without the completed items as you wanted
let todos = [
{
done: true,
name: "i need to be removed"
},
{
done: true,
name: "useless"
},
{
done: false,
name: "i need to be kept"
},
{
done: false,
name: "useful"
}];
let elements_to_delete = todos.filter(todo => todo.done);
let elements_to_keep = todos.filter(todo => !todo.done);
console.log("\n Items to keep are :-");
console.log(elements_to_keep);
console.log("\n Items to delete are :-");
console.log(elements_to_delete);
The above snippet shows how you can filter out done item then all you need to do is use setTodos(elements_to_keep)in the deleteTodo function because elements_to_keep is also an array that can replace the previous array.
You can try this:
// replace your function deleteTodo with this:
const deleteTodo = useCallback((d_todo) => {
setTodos(todos => todos.filter(todo => d_todo.id !== todo.id))
}, [])
This filters-out the todo being deleted.

State outside of my useCallback is different to the one that is inside

console.log('outside', currentPageNumber); // 0 then 3.
const fetchMoreItems = useCallback(
page => {
const { from, to } = dateModifier(selectedMonth);
const params = {
from,
to,
limit: ITEMS_PER_PAGE,
page: 3,
};
console.log('inside', currentPageNumber); // 0
if (selectedTab[ISSUES]) dispatchUserSuggestions({ ...params, type: 'issue' });
if (selectedTab[SUGGESTIONS]) dispatchUserSuggestions({ ...params, type: 'suggestion' });
},
[dispatchUserSuggestions, selectedTab, selectedMonth],
);
I need the currentPageNumber to be the new version of the state rather than the old one. I've tried adding it as a dependency to the useCallback but this puts me into an infinite loop.
Don't suppose anyone can tell what is going on?
Ideally you should add the currentPageNumber as a dependency, and solve the infinite loop. The code that causes the loop doesn't appear in your example.
If you can't, you can use a ref as an escape hutch:
const currentRef = useRef();
console.log('outside', currentPageNumber);
useEffect(() => {
currentRef.current = currentPageNumber;
}, [currentPageNumber]);
const fetchMoreItems = useCallback(
page => {
const { from, to } = dateModifier(selectedMonth);
const params = {
from,
to,
limit: ITEMS_PER_PAGE,
page: 3,
};
console.log('inside', currentRef.current);
if (selectedTab[ISSUES]) dispatchUserSuggestions({ ...params, type: 'issue' });
if (selectedTab[SUGGESTIONS]) dispatchUserSuggestions({ ...params, type: 'suggestion' });
},
[dispatchUserSuggestions, selectedTab, selectedMonth],
);

Functional component problems React

I transformed a class component into a functional component but it looks like it does not work in a way it suppose to work and I can not find what is wrong. When I create a new object there is no name for the object and when I try to mark the object as a complete it removes all created objects at ones. I created a codesandbox here. Unfortunately, I am not too much familiar with functional component. Any help would be appreciated.
Here is my codesandbox sample:
https://codesandbox.io/s/crazy-sid-09myu?file=/src/App.js
Your Todos:
const [todos, setTodos] = useState([
{ id: uuid(), name: "Task 1", complete: true },
{ id: uuid(), name: "Task 2", complete: false }
]);
onAddHandler:
const addTodo = () =>
setTodos([...todos, { id: uuid(), name: "New Task", complete: false }]);
onSetCompleteHandler:
const setCompleteHandler = id =>
setTodos(
todos.map(todo => {
if (todo.id === id) {
return {
...todo,
complete: todo.complete ? 0 : 1
};
}
return todo;
})
);
I have created your new todos. Check out this link
Todos App
I have updated your code, please check the URL https://codesandbox.io/s/determined-morning-n8lgx?file=/src/App.js
const onComp = id => {
for (let i = 0; i < todos.length; i++) {
if (todos[i].id === id) {
let t = { ...todos[i] };
t.complete = !t.complete;
todos[i] = t;
}
}
setTodos([...todos]); // Here todos reference need to be changed
};
And also
const onSubmit = event => {
event.preventDefault();
setTodos([
...todos,
{
id: generateNewId(),
name: newTodoName,
complete: false
}
]);
setNewTodoName("");
};
While using hooks we need to be careful about state variable updates. While manipulating arrays and objects use the spread operator to create new references which invokes child components and re-render current component.

How to map trough array of object and toggle boolean property selected

I have state in React functional component. It is and array of objects. Every object in that collection has property "selected", which is a boolean. That array looks like this:
const [filterOptions, setFilterOptions] = useState([
{
title: 'highest',
selected: true,
},
{
title: 'lowest',
selected: false,
},
]);
After handleFilter func is executed I need to set state so this array has same title properties but reverse (toggle) selected properties.
This is handleFilter func in which I need to toggle every selected property of array objects:
const handleFilter = () => {
setFilterOptions();
};
function App() {
const [filterOptions, setFilterOptions] = useState([
{
title: 'highest',
selected: true,
},
{
title: 'lowest',
selected: false,
},
]);
const handleFilter = (e) => {
let newArr = [...filterOptions];
let value = e.target.value;
if (value === "lowest") {
newArr[0].selected = true;
newArr[1].selected = false;
} else if (value === "highest") {
newArr[0].selected = false;
newArr[1].selected = true;
}
setFilterOptions(newArr)
};
return (
<div>
<select onChange={handleFilter}>
<option value="lowest">a</option>
<option value="highest">b</option>
</select>
{console.log((filterOptions))}
</div>
);
}
please check hope it will work
var arryObj =[
{
title: 'highest',
selected: true,
},
{
title: 'lowest',
selected: false,
},
]
const handleFilter = (index,value) => {
arryObj[index].selected = value
};
handleFilter(0,false)
console.log(arryObj)
handleFilter(1,true)
console.log(arryObj)
You can pass a function into setFilterOptions to change the state based on the previous state.
const handleFilter = () => {
setFilterOptions(prevState =>
prevState.map(obj => ({...obj, selected: !obj.selected}))
);
};

simulate change not working with trim() enzyme

I was using this test when I had a bug, so I used the trim function for resolve it, and the these test fail, tried in different ways but didn't found the solution
const generalWrapper = shallow(<AddVehiclesTable {...generalProps} />)
const generalInstance = generalWrapper.instance()
describe('onSearchChange', () => {
test('should change the "search" state', () => {
const theFilterValue = 'a new filter value'
generalWrapper.find('.filter-input').simulate('change', { target: { value: theFilterValue } })
const expectedState = Object.assign({}, generalInstance.state)
expectedState.searchValue = { 'target': { 'value': theFilterValue } }
expect(generalInstance.state).toEqual(expectedState)
expect(generalInstance.state.userInteractedWithComponent).toBe(true)
})
})
onSearchChange (searchValue) {
const value = searchValue.trim()
this.setState({ searchValue: value, userInteractedWithComponent: true })
}
Error message
TypeError: searchValue.trim is not a function
Any suggestions
Your function gets the Object as a parameter.
Expose field that you needed
I don't see the whole picture, but can guess that you need something like
onSearchChange ({ target: { value: incomeValue } }) {
const value = incomeValue.trim()
this.setState({ searchValue: value, userInteractedWithComponent: true })
}

Resources