React Redux using an object in the initialState - reactjs

I have a chat application coded in ReactJS. I want to make a redux function to add to a list of chats and messages. Hard to explain but here's the code. This is the shortened version of the code. I removed many things that were unrelated so that this code would be easier to read.
import {
CHAT_UPDATE
} from '../actions/types'
const initialState = {
conversations: {
"jack": [
{
"sender": "jack",
"text": "This is hard coded",
"date": "00:00:00 MN | May 5"
},
{
"sender": "administrator",
"text": "Noted",
"date": "00:00:01 AM | May 5"
}
]
}
}
export default (state = initialState, action) => {
switch (action.type) {
case CHAT_UPDATE:
return {
...state,
conversations: {
// HELP
}
}
default:
return state
}
}
How do I make an update function so that if an action of CHAT_UPDATE is dispatched, - which contains the information of the requested chat and the new message - it will add it to the list of conversations?
For example when this is dispatched:
dispatch({
type: CHAT_UPDATE,
name: 'jack',
coversation: {
"sender": "jack",
"text": "How's your day?",
"date": "00:00:02 AM | May 5"
}
})
I want the new state to be
conversations: {
"jack": [
{
"sender": "jack",
"text": "This is hard coded",
"date": "00:00:00 MN | May 5"
},
{
"sender": "administrator",
"text": "Noted",
"date": "00:00:01 AM | May 5"
},
{
"sender": "jack",
"text": "How's your day?",
"date": "00:00:02 AM | May 5"
}
]
}

This will work!!
return {
...state,
conversations: {
...state.converstation,
[action.name]: [
...state.converstation[action.name],
{
...action.coversation
}
]
}
}

You should update it in this way
return {
...state,
[
...state[action.name],
action.coversation,
]
}

Related

Please tell me how to resolve a type error

I'm writing a program that turns two data into a new one.
Each of the data comes from an API, so the undefined case must be considered.
but,
item.id === message.sender.userId
A type error occurs in userId in the section.
I want to resolve this type error.
I want to solve it by a means that does not change the type or data.
Is this possible?
const newMessages = React.useMemo(() => {
if (messages === undefined || userItems === undefined) return [];
return messages.map(message => {
if (message.sender.type === "user" && message.sender.userId) {
const user = userItems.find(item => item.id === message.sender.userId);
if (user) message.sender = { ...message.sender, icon: user.image };
}
return message;
});
}, [messages, userItems]);
Here are the two data I'm getting, and the type of one:
type IMessage = {
sender:
| {
type: "user";
userId: number;
}
| {
type: "admin";
adminId: number;
};
body:
| {
type: "text";
text: "text";
}
| {
type: "image";
text: string;
image: string;
};
};
const messages: IMessage[] =
[
{
"sender": {
"type": "admin",
"adminId": 789
},
"body": {
"type": "image",
"text": "abcde",
"image": "https://imageUrl"
},
},
{
"sender": {
"type": "user",
"userId": 10
},
"body": {
"type": "text",
"text": "Hello!"
},
},
{
"sender": {
"type": "user",
"userId": 13
},
"body": {
"type": "image",
"text": "Hello my friend!",
"image": "https://imageUrl"
},
},
]
const userItems =
[
{
"id": 10,
"name": "kenny",
"image": "https://imageUrl",
"age": 23,
"gender": "M",
},
{
"id": 13,
"name": "Jon",
"image": "https://imageUrl",
"age": 32,
"gender": "M",
}
]
const user = userItems.find(item => item.id === message.sender.userId);
You and i know that this function is going to be called right away, but typescript does not. In general, a callback function could be called at any time, synchronously or asynchronously, and nothing in the type information specifies that. As a result, typescript cannot guarantee that message.sender will still be a user when the callback gets called.
To fix this, assign the value to a const, so typescript can know for sure it won't change.
if (message.sender.type === "user" && message.sender.userId) {
const userId = message.sender.userId
const user = userItems.find(item => item.id === userId);
// ...
P.S:
body:
| {
type: "text";
text: "text";
}
You probably meant to do text: string

How do I sort this array by date?

I'm trying to sort the dates from this external API in my latestResults array by latest on top to oldest on bottom but can't seem to figure out how.
Right now they're displayed with the oldest date first and it's working fine, but it's in the wrong order for me.
I tried using result in latestResults.reverse() but that just reverses the 7 items currently in the array.
HTML:
<div v-for="result in latestResults" v-bind:key="result.latestResults">
<small">{{ result.utcDate }}</small>
</div>
Script:
<script>
import api from '../api'
export default {
data () {
return {
latestResults: [],
limit: 7,
busy: false,
loader: false,
}
},
methods: {
loadMore() {
this.loader = true;
this.busy = true;
api.get('competitions/PL/matches?status=FINISHED')
.then(response => { const append = response.data.matches.slice(
this.latestResults.length,
this.latestResults.length + this.limit,
this.latestResults.sort((b, a) => {
return new Date(b.utcDate) - new Date(a.utcDate);
})
);
setTimeout(() => {
this.latestResults = this.latestResults.concat(append);
this.busy = false;
this.loader = false;
}, 500);
});
}
},
created() {
this.loadMore();
}
}
</script>
The JSON where I'm getting matches like this that has utcDate:
{
"count": 205,
"filters": {
"status": [
"FINISHED"
]
},
"competition": {
"id": 2021,
"area": {
"id": 2072,
"name": "England"
},
"name": "Premier League",
"code": "PL",
"plan": "TIER_ONE",
"lastUpdated": "2021-02-01T16:20:10Z"
},
"matches": [
{
"id": 303759,
"season": {
"id": 619,
"startDate": "2020-09-12",
"endDate": "2021-05-23",
"currentMatchday": 22
},
"utcDate": "2020-09-12T11:30:00Z",
"status": "FINISHED",
"matchday": 1,
"stage": "REGULAR_SEASON",
"group": "Regular Season",
"lastUpdated": "2020-09-13T00:08:13Z",
"odds": {
"msg": "Activate Odds-Package in User-Panel to retrieve odds."
},
},

Get API Json data in object and array format using redux and reactjs

I am successful in getting the data from the sample code in the below tutorial with Redux.
https://codepen.io/stowball/post/a-dummy-s-guide-to-redux-and-thunk-in-react
Below is the format used in the tutorial which works great.
[
{
"id": 1,
"label": "List item 1"
},
{
"id": 2,
"label": "List item 2"
},
{
"id": 3,
"label": "List item 3"
},
{
"id": "4",
"label": "label 4"
},
{
"id": "5",
"label": "label 5"
}
]
But I tried several ways and unable to pass the data in the format below
{
"banner": [
{ "key": "product", "id": "84535", "image_url": "/media/emthemes/slideshow/s/u/sultry-app_12.jpg" },
{ "key": "category", "id": "2629", "image_url": "/media/emthemes/slideshow/l/i/limecrime-app.jpg" },
{ "key": "product", "id": "84654", "image_url": "/media/emthemes/slideshow/a/m/ameera-app-banner.jpg" }
]
}
Current Reducer and mapping component in another file :
export function items(state = [], action) {
switch (action.type) {
case 'ITEMS_FETCH_DATA_SUCCESS':
return action.items;
default:
return state;
}
}
return (
<ul>
{this.props.items.map((item) => (
<li key={item.id}>
{item.id}
</li>
))}
</ul>
);
Tried with below changes in reducer:
export function items(state = {banner:[]}, action) {
switch (action.type) {
case 'ITEMS_FETCH_DATA_SUCCESS':
return action.items;
default:
return state;
}
}
change reducer to this:
{this.props.items.banner.map((item) => (
<li key={item.id}>
{item.id}
{item.key}
{item.image_url}
</li>
))}
And change the PropTypes of items in ItemList.js
items: PropTypes.object.isRequired
I have tried the tutorial and created a working example here:
https://github.com/stackOverflow166/redux-nwb
Edit2:
Cannot read property 'map'
This is problem is about the date type.Add this to the ItemList.js to change the date type to array.Only array has the map function.
let bannerArray = []
let banner = this.props.items.banner
for (let key in banner){
bannerArray.push(banner[key])
}

React setState - Add array to nested object with multiple arrays

I'm currently working on a new application in React. This is the first time I'm creating something in React. The application will display our own promotions.
My initial state is as follows:
{
"promotion": {
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"startDateTimeStamp": 1510558814960,
"endDateTimeStamp": 1510558814960,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
}
This is created from my defaultPromotion constant. This constant is stored in a separate file, which I call api.js
export const defaultPromotion = {
name: '',
campaign: '',
url: 'https://',
position: 0,
periods: [
{
startDateTimeStamp: Date.now(),
endDateTimeStamp: Date.now(),
variants: [
{
title: '',
text: '',
image: '',
},
]
},
]
}
In my createPromotion component it's created as followed
let promotionState = api.promotions.defaultPromotion;
this.state = {
promotion: promotionState
};
I can add a new period with the following:
addPromotion() {
let promotion = this.state.promotion;
promotion.periods.push( api.promotions.defaultPromotion.periods[0] );
this.forceUpdate();
}
After that, a new period is added as expected. Suggestions to do this with setState() are very welcome! So, my new state is now:
{
"promotion": {
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
},
{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
}
Now, I want to add a new variant for this promotion period, this is where I'm stuck for 2 days now.
I'm adding a new period as follows:
addVariant( periodKey ) {
const promotion = this.state.promotion;
promotion.periods[periodKey].variants.push(api.promotions.defaultPromotion.periods[0].variants[0]);
this.setState({ promotion: promotion });
}
periodKey is here "1", so, I'm expecting that there will be added a new variant for periods[1], but, it's added to both periods. State is now as follows:
{
"promotion": {
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [
{
"title": "",
"text": "",
"image": ""
},
{
"title": "",
"text": "",
"image": ""
}
]
},
{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [
{
"title": "",
"text": "",
"image": ""
},
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
}
Can someone explain me why this is happening and how I can add a new variant the right way?
Many, many thanks in advance!
UPDATE 1
Based on the answers from bennygenel and Patrick Hübl-Neschkudla, my implementation is now as follows:
Setting the initial state:
constructor(props) {
super(props);
let promotionState = api.promotions.defaultPromotion;
this.state = { ...promotionState };
}
Method:
addVariant( periodKey ) {
this.setState((prevState) => {
const { periods } = prevState;
periods[periodKey].variants.push(
Object.assign({}, { ...periods[periodKey].variants, api.promotions.defaultPromotion.periods[0].variants[0]})
);
return { periods };
});
}
But this still is setting the new variant in all the periods. I've also tried the exact code from Benny, but with the same results. The method is called as
this.props.addVariant( this.props.periodKey );
Even when I call it as:
this.props.addVariant(2);
The same behaviour is happening.
UPDATE 2
I now have rewritten everything to redux, this is so I have access to my promotion in every component the easy way, instead off passing them through certain components. Based on the answer of #mersocarlin, I now have the following reducer cases:
Add period
case PROMOTION_ADD_PERIOD:
const { periods } = { ...state };
periods.push(api.promotions.defaultPromotion.periods[0]);
state = {
...state,
periods: periods
};
break;
Add a period variant
case PROMOTION_ADD_PERIOD_VARIANT :
state = {
...state,
periods: [
...state.periods[action.payload.period],
{
variants: [
...state.periods[action.payload.period].variants,
api.promotions.defaultPromotion.periods[0].variants[0]
]
}
]
};
break;
The following case:
Add a new variant, works, state:
{
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"startDateTimeStamp": 1510599968588,
"endDateTimeStamp": 1510599968588,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
},
{
"startDateTimeStamp": 1510599968594,
"endDateTimeStamp": 1510599968594,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
After that, adding a new variant, kinda works, well, the variant is added, but I'm losing my 2nd period. State:
{
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"variants": [
{
"title": "",
"text": "",
"image": ""
},
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
I think this is a small thing I'm not see'ing. Does someone have the solution for the "PROMOTION_ADD_PERIOD_VARIANT" case?
Update 3
Changed the "PROMOTION_ADD_PERIOD" case as follows:
case PROMOTION_ADD_PERIOD:
state = {
...state,
periods: [
...state.periods,
initialState.periods[0]
]
};
break;
Update 4
Finaly found the solution. See the final code for PROMOTION_ADD_PERIOD_VARIANT below:
state = {
...state,
periods: [
...state.periods.map((item, index) => {
if ( index !== action.payload.period ) {
return item;
}
return {
...item,
variants: [
...item.variants,
initialState.periods[0].variants[0]
]
}
})
]
};
Thank you all so much for your help!!
Rather destruct your state object and avoid mutating it directly. This also happens to be a bad pattern.
Whenever you need to add a new item to the array:
const state = {
arrayProp: [{ prop1: 'prop1', prop2: 'prop2' }]
}
const newItem = {
prop1: 'value1',
prop2: 'value2',
}
const newState = {
...state,
arrayProp: [
...state.arrayProp,
newItem,
]
}
console.log('newState', newState)
Same applies for nested properties within your state:
Redux also uses this very same approach
const state = {
objectProp: {
arrayPropWithinArray: [
{ id: '0', otherProp: 123, yetAnotherProp: 'test' },
{ id: '1', otherProp: 0, yetAnotherProp: '' }
]
}
}
const { objectProp } = state
const index = objectProp.arrayPropWithinArray.findIndex(obj => obj.id === '1')
const newSubItem = {
otherProp: 1,
yetAnotherProp: '2',
}
const newState = {
...state,
objectProp: {
...objectProp,
arrayPropWithinArray: [
...objectProp.arrayPropWithinArray.slice(0, index),
{
...objectProp.arrayPropWithinArray[index],
...newSubItem,
},
...objectProp.arrayPropWithinArray.slice(index + 1),
]
}
}
console.log('newState', newState)
Your specific case (as described in your comment)
const periodKey = '2' // your periodKey var. Get it from the right place, it can be your action for example
const index = state.periods.findIndex(period => period.id === periodKey) // find which index has to be updated
state = {
...state, // propagates current state
periods: [
...state.periods.slice(0, index), // propagates everything before index
{
...state.periods[index],
variants: [
...state.periods[index].variants,
api.promotions.defaultPromotion.periods[0].variants[0],
],
},
...state.periods.slice(0, index + 1) // propagates everything after index
]
}
So, what's happening here is that you have an array with two references to the same object.
Imagine it like this:
myArray[0] = reference to defaultPromotion
myArray[1] = reference to defaultPromotion
That's actually a wonderful example of why immutability concepts got so much attention in the past few years :)
What you'd want to do here is instead of adding the defaultPromotion object to the promotions array, you create a new object with the same props as this object and add it. It would look something like this (depending on your ES version etc.)
promotion.periods.push(
Object.assign({}, api.promotions.defaultPromotion.periods[0])
);
This way, you're creating a new object and pass this to the array instead of a reference to the already existing one.
First suggestion, if you are going to have only one promotion object in your state and not an array, lose the promotion level. this will reduce the complexity of your state. You can use spread syntax to easily set your initial state.
Example
let promotionState = api.promotions.defaultPromotion;
this.state = { ...promotionState };
Above code would end up creating a state like below;
{
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [{
"title": "",
"text": "",
"image": ""
}]
}, {
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [{
"title": "",
"text": "",
"image": ""
}]
}]
}
Another suggestion I can make is to use functional setState to reduce possibility to mutate.
Example
addPromotion() {
this.setState((prevState) => {
const { periods } = prevState;
periods.push(api.promotions.defaultPromotion.periods[0]);
return { periods };
});
}
addVariant( periodKey ) {
this.setState((prevState) => {
const { periods } = prevState;
periods[periodKey].variants.push(api.promotions.defaultPromotion.periods[0].variants[0]);
return { periods };
});
}

Angular2 filter object array based on value inside property(nested filters)

I need to retrieve only the object that have the name ... as a participant using a filter(pipe).
Every object has an array of participants objects. This is what it looks like(see my example Json at the end)
-object
-- _id
-- participants
-- participant1
-- participant2
So this is what I tried:(hardcoded Jack to get a match...)
import { Pipe, PipeTransform } from "#angular/core";
#Pipe({ name: 'filter' })
export class Gamefilter implements PipeTransform {
public transform(values: any[], filter: string): string {
if (!values || !values.length) "";
if (!filter) return JSON.parse(JSON.stringify(values));
return JSON.parse(JSON.stringify(
values.filter((element) => {
return element.participants.filter((item)=> {
return item.name.indexOf("Jack") > 1;
});
})
));
}
}
Altough this doesn't work, and I can't see the mistake I made.
I tried this to (from Filtering array of objects with arrays based on nested value ):
values.filter((element) =>
element.participants.some((subElement) => subElement.name === "Jack"))
.map(element => {
let newElt = Object.assign({}, element); // copies element
return newElt.participants.filter(subElement => subElement.name === "Jack");
})
I also saw this post but could get the right answer for me: Filtering array based on value in deeply nested object in javascript
The example JSON:
[
{
"_id": "5925ae95675e19001106e940",
"createdOn": "2017-05-24T16:02:29.229Z",
"participants": [
{
"_id": "jack19302",
"name": "Jack",
"__v": 0
},
{
"_id": "donald38902",
"name": "Donald",
"__v": 0
}
]
},
{
"_id": "5925ae95675e19001106e990",
"createdOn": "2017-05-24T16:02:29.229Z",
"participants": [
{
"_id": "donald38902",
"name": "Donald",
"__v": 0
}
]
},
{
"_id": "5925ae95675e19001106e996",
"createdOn": "2017-05-24T16:02:29.229Z",
"participants": [
{
"_id": "jack19302",
"name": "Jack",
"__v": 0
}
]
}
]

Resources