I am working on project of Food app. I have preference screen where user can click on any desired preference to add in order. This is working but I want to dispatch a action so the clicked data is stored inside of redux store.
User can only select few from given like 2 or 3.
This is the function i am using for only few item slection logic
{item.map((items, index) => (
<TouchableOpacity
key={items.id}
onPress={() => {
const id=preferenceData.indexOf(item)
//console.log("id=",id)
// console.log("itemsID",items.checked)
if (minimum_quatity[id] !== null) {
//console.log(" 1st loop enter")
preferenceData[id].forEach((i) => {
const check=item.filter(items =>items.checked ? items : null)
if (i.id == items.id) {
// console.log("checked",check)
// console.log("condition",(check.length < minimum_quatity[id]))
if (check.length < minimum_quatity[id]) {
console.log("before" ,i.checked)
i.checked=!i.checked;
console.log("after", i.checked)
selectItem(i, i.checked, ranid,i.name)
}
else {
console.log("falsed")
i.checked = false;
selectItem(i, i.checked, ranid,i.name)
}
}
setpreferenceData([...preferenceData])
});
setcounter(counter[id] + 1);
console.log("itemsID",items.checked);
}
else {
preferenceData[id].forEach((i) => {
if (i.id === items.id) {
i.checked = !i.checked;
selectItem(i, i.checked, ranid,i.name)
console.log("null")
}
});
setpreferenceData([...preferenceData]);
}
}}
>
This runs perfectly fine when runs first time but if i diselect a radio button and again selects it then it data is never sent in redux SelectItem action .
This is action selectItem
const selectItem = (checkeditems, checkboxValue, id, name) => {
dispatch({
type: 'ADD_TO_CART',
payload: {
...checkeditems,
checkboxValue: checkboxValue,
id: id,
name: name,
},
});
};
My reducer file is fine because i have used it and it works fine.
I know that i should have used redux tool kit but just for now i am using it for learning purpose
switch (action.type) {
case 'ADD_TO_CART': {
let newState = { ...state };
if (action.payload.checkboxValue) {
console.log('ADD TO CART',action.payload);
newState.selectedItems = {
checkeditems: [
...newState.selectedItems.checkeditems,
action.payload,
],
items: [...newState.selectedItems.items],
quantity:[...newState.selectedItems.quantity]
};
console.log('ADD TO CART return');
} else {
console.log('REMOVE FROM CART');
newState.selectedItems = {
checkeditems: [
...newState.selectedItems.checkeditems.filter(
(item) => (item.name !== action.payload.name)
),
],
items: [...newState.selectedItems.items],
};
}
console.log(newState, '👉');
return newState;
}
The ouput is attached .I pressed two radio buttons and the deselected them and output is fine .It adds to cart and remove from cart.
but after it when there again selects any radio button it does nothing
Output image
Related
My problem is that I want to insert values that are not repeated when doing a push
This is my code :
addAddress: function() {
this.insertAddresses.Adress = this.address_address
this.insertAddresses.State = this.selectedStateAddress
this.insertAddresses.City = this.selectedCityAddress
if(this.insertAddresses.Adress !== "" && this.insertAddresses.State !== null && this.insertAddresses.City !== null) {
let copia = Object.assign({}, this.insertAddresses);
this.addresses.push(copia)
}
else
{
this.$message.error('Not enough data to add');
return
}
},
When adding a new element to my object, it returns the following.
When I press the add button again, it adds the same values again, I want to perform a validation so that the data is not the same. How could I perform this validation in the correct way?
Verify that the item doesn't already exist in the array before inserting.
You can search the array using Array.prototype.find:
export default {
methods: {
addAddress() {
const newItem = {
Address: this.address_address,
State: this.selectedStateAddress,
City: this.selectedCityAddress
}
this.insertItem(newItem)
},
insertItem(item) {
const existingItem = this.addresses.find(a => {
return
a.State === item.State
&& a.City === item.City
&& a.Address === item.Address
})
if (!existingItem) {
this.addresses.push(item)
}
}
}
}
On the other hand, if your app requires better performance (e.g., there are many addresses), you could save a separate dictonary to track whether the address already exists:
export default {
data() {
return {
seenAddresses: {}
}
},
methods: {
insertItem(item) {
const { Address, State, City } = item
const key = JSON.stringify({ Address, State, City })
const seen = this.seenAddresses[key]
if (!seen) {
this.seenAddresses[key] = item
this.addresses.push(item)
}
}
}
}
demo
check it:
let filter= this.addresses.find(x=> this.insertAddresses.State==x.State)
if (filter==null) {
this.$message.error('your message');
}
OR FILTER ALL
let filter= this.addresses.find(x=> this.insertAddresses.Adress==x.Adress && this.insertAddresses.State==x.State && this.insertAddresses.City==x.City)
if (filter==null) {
this.$message.error('your message');
}
``
I am trying to get some statistics and problems for a user using a Redux action and pass it to a React component. The problem is, I have the array of objects curPageExercisesMarked, which I use for the pagination of the page, but it does not take the values I assign it to.
The stranger thing is that the other fields in the Redux store get updated, but not this one. I tried consoling the object in the action, but it just prints this:
It is important to mention that I am doing something similar in another action, using the exact same assignment and it works there. I've lost already an hour trying to figure this thing out so any help is welcomed.
The Redux action:
export const setStatistics = (
problems,
problemsSolved,
filter = ''
) => dispatch => {
let payload = {
subject1: 0,
subject2: 0,
subject3: 0,
total: 0,
exercisesMarked: [],
curPageExercisesMarked: []
};
for (let i = 0; i < problems.length; i++) {
if (problems[i].S === '1' && problemsSolved.includes(problems[i]._id)) {
payload.subject1++;
payload.total++;
payload.exercisesMarked.push(problems[i]);
} else if (
problems[i].S === '2' &&
problemsSolved.includes(problems[i]._id)
) {
payload.subject2++;
payload.total++;
payload.exercisesMarked.push(problems[i]);
} else if (
problems[i].S === '3' &&
problemsSolved.includes(problems[i]._id)
) {
payload.subject3++;
payload.total++;
payload.exercisesMarked.push(problems[i]);
}
}
payload.curPageExercisesMarked = payload.exercisesMarked.slice(0, 10);
dispatch({
type: SET_USER_STATISTICS,
payload
});
};
The redux reducer:
export default function(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case SET_USER_STATISTICS:
return {
...state,
exercisesMarked: payload.exercisesMarked,
curPageExercisesMarked: payload.curPageExercisesMarked,
subject1: payload.subject1,
subject2: payload.subject2,
subject3: payload.subject3,
total: payload.total
};
case CHANGE_PAGE_MARKED:
return {
...state,
page: payload,
curPageExercisesMarked: state.exercisesMarked.slice(
(payload - 1) * state.pages_count,
payload * state.pages_count
)
};
default:
return state;
}
}
This is the part that does not function:
payload.curPageExercisesMarked = payload.exercisesMarked.slice(0, 10);
EDIT
I've discovered that if I go a component which loads all the problems and come back to this component, it actually gets the correct value.
Now, the interesting is that I do get the same problems here as well. Is it the way I use React Hook?
This is the part where I call the redux action in the react component:
const Dashboard = ({
problems: { problems },
auth: { user },
getProblems,
dashboard: {
curPageExercisesMarked,
page,
exercisesMarked,
pages_count,
subject1,
subject2,
subject3,
total
},
setStatistics
}) => {
useEffect(() => {
if (problems === null) {
getProblems();
} else if (user !== null) {
setStatistics(problems, user.problemsSolved);
}
}, [problems, user]);
// rest of the code
}
You can first simplify code as below. Update/Print console.log(JSON.stringify(payload)). I think if(problemsSolved.includes(problems[i]._id)) not working as expected
export const setStatistics = (
problems,
problemsSolved,
filter = ""
) => dispatch => {
let payload = {
subject1: 0,
subject2: 0,
subject3: 0,
total: 0,
exercisesMarked: [],
curPageExercisesMarked: []
};
for (let i = 0; i < problems.length; i++) {
if(problemsSolved.includes(problems[i]._id)) {
payload["subject"+ problems[i].S]++
payload.total++;
payload.exercisesMarked.push(problems[i]);
}
}
payload.curPageExercisesMarked = payload.exercisesMarked.slice(0, 10);
dispatch({
type: SET_USER_STATISTICS,
payload
});
};
// Also
case SET_USER_STATISTICS:
return {
...state,
...payload
};
im using redux in an react app. Why is this filtering func mutating the original state.products? I cant understand why
state.products = [
{
type: "one",
products: [
{ active: true },
{ active: false }
]
}
]
function mapStateToProps(state) {
const test = state.products.filter((item) => {
if(item.type === "one") {
return item.products = item.products.filter((item) => {
item.active
});
}
return item;
});
return {
machineSearchWeightRange: state.machineSearchWeightRange,
filteredItems: test //This will have only products active
};
}
filteredItems will have only products that is active but the state.products is also updated containing only active products when trying to filter on the same data again.
Suggestions
Because you're assigning to a property on an existing state item:
function mapStateToProps(state) {
const test = state.products.filter((item) => {
if(item.type === "one") {
return item.products = item.products.filter((item) => { // <========== Here
item.active
});
}
return item;
});
return {
machineSearchWeightRange: state.machineSearchWeightRange,
filteredItems: test //This will have only products active
};
}
Instead, create a new item to return. Also, it looks like you need map along with filter, and you're not actually returning item.active in your inner filter (see this question's answers for more there):
function mapStateToProps(state) {
const test = state.products.filter(({type}) => type === "one").map(item => {
return {
...item,
products: item.products.filter((item) => {
return item.active;
})
};
});
return {
machineSearchWeightRange: state.machineSearchWeightRange,
filteredItems: test //This will have only products active
};
}
Side note: This:
products: item.products.filter((item) => {
return item.active;
})
can be simply:
products: item.products.filter(({active}) => active)
Objective : i have a button named "feed data" so when ever i click it the data will be loaded i mean the tree with checkboxes here my requirement is when ever i click it along with data all the check boxes have to be checked on init i tried using
this.treeComp.treeModel.doForAll((node: TreeNode) => node.setIsSelected(true));
but it is not working below is my code
click(tree: TreeModel) {
this.arrayData = [];
let result: any = {};
let rs = [];
console.log(tree.selectedLeafNodeIds);
Object.keys(tree.selectedLeafNodeIds).forEach(x => {
let node: TreeNode = tree.getNodeById(x);
// console.log(node);
if (node.isSelected) {
if (node.parent.data.name) //if the node has parent
{
rs.push(node.parent.data.name + '.' + node.data.name);
if (!result[node.parent.data.name]) //If the parent is not in the object
result[node.parent.data.name] = {} //create
result[node.parent.data.name][node.data.name] = true;
}
else {
if (!result[node.data.name]) //If the node is not in the object
result[node.data.name] = {} //create
rs.push(node.data.name);
}
}
})
this.arrayData = rs;
tree.selectedLeafNodeIds = {};
}
selectAllNodes() {
this.treeComp.treeModel.doForAll((node: TreeNode) => node.setIsSelected(true));
// firstNode.setIsSelected(true);
}
onTreeLoad(){
console.log('tree');
}
feedData() {
const results = Object.keys(this.data.info).map(k => ({
name: k,
children: this.data.info[k].properties
? Object.keys(this.data.info[k].properties).map(kk => ({ name: kk }))
: []
}));
this.nodes = results;
}
feedAnother() {
const results = Object.keys(this.dataa.info).map(k => ({
name: k,
children: this.dataa.info[k].properties
? Object.keys(this.dataa.info[k].properties).map(kk => ({ name: kk }))
: []
}));
this.nodes = results;
}
onActivate(event) {
this.selectedDataList.push(event.node.data);
console.log(this.selectedDataList)
}
onDeactivate(event) {
const index = this.selectedDataList.indexOf(event.node.data);
this.selectedDataList.splice(index, 1);
console.log(this.selectedDataList)
}
below is my stackblitz https://stackblitz.com/edit/angular-hrbppy
Use updatedata and initialized event to update the tree view to check all checkboxes.
app.component.html
<tree-root #tree *ngIf ="nodes" [nodes]="nodes" [options]="options" [focused]="true"
(initialized)="onTreeLoad()"
(updateData)="updateData()"
(select)="onActivate($event)"
(deselect)="onDeactivate($event)">
</tree-root>
It'll initiate tree-root component only if nodes variable is available,
then in the initialized and updateData event call selectAllNodes method to select all checkboxes.
app.component.ts
updateData() {
this.selectAllNodes();
}
onTreeLoad(){
this.selectAllNodes();
}
Refer to this slackblitz for working example.
just, in your function feed data call to your function this.selectAllNodes() enclosed in a setTimeout. You can see your forked stackblitz
setTimeout(()=>{
this.selectAllNodes()
})
NOTE: I see in your code you try to control in diferents ways the items selected. I simplified using a recursive function.
In this.treeComp.treeModel.selectedLeafNodeIds we have the items that are changed, so
getAllChecked()
{
const itemsChecked=this.getData(
this.treeComp.treeModel.selectedLeafNodeIds,null)
console.log(itemsChecked);
}
getData(nodesChanged,nodes) {
nodes=nodes||this.treeComp.treeModel.nodes
let data: any[] = []
nodes.forEach((node: any) => {
//in nodesChanged we has object like {1200002:true,123132321:false...}
if (nodesChanged[node.id]) //can be not changed, and then it's null because
//it's not in object or can be changed to false
data.push({id:node.id,name:node.name})
//or data.push(node.name); //if only need the "name"
if (node.children)
data=[...data,...this.getData(nodesChanged,node.children)]
}
);
return data
}
Updated I updated the function getData to include the "parent" of the node, but looking the code of #Raghul selvam, his function like me more than mine.
getData(nodesChanged,nodes,prefix) {
nodes=nodes||this.treeComp.treeModel.nodes
let data: any[] = []
nodes.forEach((node: any) => {
if (nodesChanged[node.id])
data.push(prefix?prefix+"."+node.name:node.name)
if (node.children)
data=[...data,...this.getData(nodesChanged,node.children,prefix?prefix+"."+node.name:node.name)]
}
);
return data
}
And call it as
this.getData(this.treeComp.treeModel.selectedLeafNodeIds,null,"")
You could add this in your onTreeLoad function. You could add a boolean flag(treeLoaded) for tracking if the tree has loaded or not.
onTreeLoad(tree){
this.selectAllNodes();
this.treeLoaded = true;
}
I make a component, which show information from database in table. But this information with filters.
Filtering can be by event type and by participant (id: integer type).
When I click the button, I call handleShowClick(). In this function I check: if value of type event isn't null, I get from database events with this type. if value of type event is null, I get all events.
After this I check a participant value. If value isn't null, I call function, which search which events are include this participant. Data from this.state.event show in table in another component.
I haven't problems with event type. But I have problem with participant. When I choose one of participant, table shows correct data for a split second. After this return to prev state (without filter by participants).
How can I fix this issue? I set state to event only in this component
class TestPage extends Component {
constructor(props) {
super(props);
this.state = {
event: [],
searchByType: null,
searchByParticipant: null,
participantToEvent: []
};
this.handleShowClick = this.handleShowClick.bind(this);
this.onHandleEventByTypeFetch = this.onHandleEventByTypeFetch.bind(this);
this.handleParticipantSearch = this.handleParticipantSearch.bind(this);
this.onHandleEventFetch = this.onHandleEventFetch.bind(this);
}
handleShowClick() { // onClick
if (this.state.searchByType !== null) {
this.onHandleEventByTypeFetch(); // select * from ... where type=...
} else {
this.onHandleEventFetch(); // select * from ...
}
if (this.state.searchByParticipant !== null) {
this.handleParticipantSearch();
}
}
handleParticipantSearch() {
const list = [];
this.state.participantToEvent.map(itemP => { // participantToEvent is binding table
if (itemP.parid === this.state.searchByParticipant) {
this.state.event.map(itemEvent => {
if (itemEvent.id === itemP.eventid) {
list.push(itemEvent);
}
});
}
});
console.log(list); // here I see array with correct result
this.setState({ event: list });
}
onHandleEventFetch() {
fetch( ... , {
method: 'GET'
})
.then((response) => {
if (response.status >= 400) {
throw new Error('Bad response from server');
}
return response.json();
})
.then(data => {
if (data.length === 0) {
alert('nothing');
} else {
this.setState({
event: data
});
}
});
}
onHandleEventByTypeFetch() {
fetch( ... , {
method: 'GET'
})
.then((response) => {
if (response.status >= 400) {
throw new Error('Bad response from server');
}
return response.json();
})
.then(data => {
if (data.length === 0) {
alert('nothing');
} else {
this.setState({
event: data
});
}
});
...
}
}
Structure of this.state.event:
[{id: 1, name: 'New event', participant: 5, type: 10}, ...]
Structure of this.state.participantToEvent:
[{id: 1, idparticipant: 5, idevent: 1}, ...]
this.setState(...this.state,{ event: list });
I think this would solve your problem. Because you clear every item except for {event:list} by not copying the previous state.
Edit:
You should put
...this.state
to onHandleEventByeTypeFetch and onHandleEventFetch. Without them when you click handleShowClick one of those two functions always work and clears searchByParticipant data from the state by not copying the previous state.
The reason for you see the correct data for a short time is all about async nature of the state.