push state object into state array in reactJS application - reactjs

I have a store file which contains states. I defined user like this:
user: store.getState().user || { jobRecord: [] },
What I want is to push some job objects into user.jobRecord. The job object is defined like this:
job: store.getState().job || { title: '', company: '', start_date:
'', end_date: '' }
So first of all, I set some value into job object and then push it into user.jobRecord.
const joined = user && user.jobRecord;
store.setState({
user: {
...user,
jobRecord: [...joined, job],
},
});
By doing this, I get type error:
TypeError: joined is not iterable
I try this to set just one record in it:
jobRecord: [job],
but the result of jobRecord is:
[object Object]
Thank you a lot for considering my issue.

You are checking for falsey value in store.getState().user which is actually an object. Therefore it will always be true and jobRecord would never be filled and [...user] would give error as you are destructuring on empty object.
user: store.getState().user || { jobRecord: [] },
To avoid this issue use Object.keys(this.state.user).length to check if object is empty or not.
Working code below:-
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
state = {
job: {},
user: {}
}
componentDidMount () {
let user= Object.keys(this.state.user).length || { jobRecord: [] };
let job= this.state.job || {
title: '',
company: '',
start_date: '',
end_date: ''
};
const joined = user && user.jobRecord;
this.setState({
user: {
...user,
jobRecord: [...joined, job],
},
});
}
render () {
console.log('inside user',this.state.user)
return (
<div className="App">
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

It is not possible to iterate undefined value
const joined = user && user.jobRecord assigns undefined value to joined variable when user or user.jobRecord in undefined
To fix it, please use following code instead:
const joined = user && user.jobRecord || [];

Related

How do I update a store that is in local storage with SvelteKit?

I am trying to update a store that is in local storage when I fetch data from Supabase.
The store always appears blank in the browser inspector even after using set() or update() or just by doing store.value = anotherValue. I have looked over the docs for this and looked through different tutorials but nothing has this specific use case.
If I do let firmNameVar = firmStore.firmName then I can get the value but I still don't see it in the browser inspector in local storage.
The Store Code:
import { writable } from "svelte/store";
import { browser } from "$app/environment";
export const storedFirm = JSON.parse(browser && localStorage.getItem("firmStore")) || {
firmID: '',
firmName: '',
firmAddress: '',
firmPhoneNumber: '',
firmWebsite: '',
firmType: '',
firmMembers: [''],
firmProjects: [''],
firmRoles: [''],
firmPermissionsID: '',
firmComplete: Boolean,
completeYourProfile: Boolean,
buildYourTeam: Boolean,
definePermissions: Boolean,
aboutYourWork: Boolean,
errorConnectingToSupabase: Boolean
};
export const firmStore = writable(browser && storedFirm);
firmStore.subscribe(
(val) => browser && (localStorage.firmStore = JSON.stringify(val))
);
Code to Update the Store:
import { firmStore } from "/src/stores/firmStore";
///CONNECTING TO DATABASE
async function queryFirmDatabase(){
const { data: firm, error } = await supabaseClient
.from('firms')
.select('id, firm_name,firm_address, firm_phone, firm_website, firm_projects, firm_complete, complete_your_profile, build_your_team, define_permissions, about_work')
.eq('id', clientUser.orgID)
if (firm){
///CONNECTED TO DATABASE AND TRYING TO ASSIGN VALUE TO STORE OBJECT
const [fetchedUser] = firm;
try {
firmStore.firmName.set(fetchedUser.firm_name);
} catch (error) {
console.log("Doesn't work")
}
} else {
console.log("THERE WAS AN ERROR FETCHING FROM FIRM DATABASE")
console.log(error)
}
}
This is simply not valid:
localStorage.firmStore = JSON.stringify(val)
You need to use localStorage.setItem(key, value).

How do you bind a dynamic API response to a React UI?

I am consuming an API which has a few 'required' fields and fields which are not required. As such, depending on the result of the response, the shape can change dramatically with fields being excluded in the response.
An example Schema:
{
name: string; // required
surname: string; // required
addresses: []
telephoneNumbers: []
gender: string; // required
age: number;
}
So a request could return multiple formats:
Example: 'api.com/users/123'
{
name: 'John',
surname: 'Doe',
addresses: [ ....]
telephoneNumbers: [....]
gender: 'male',
age: 20
}
Example: 'api.com/users/456
{
name: 'Betty',
surname: 'Boo',
gender: 'female',
}
When I bind the response to my UI (react) is there a way to check each field exists?
Do I have to check if each field exists and has a value in the response before rendering it? As in:
const DisplayResults = () => {
const { data } = await api.getUser('123');
return (
<div>
<div>{data.name}</div>
<div>{data.surname}</div>
{data.age && <div>{data.age}</div>}
{data.gender && <div>{data.age}</div>}
{data.addresses && <div>{data.addresses.map((address) => ...)}</div>}
</div>
)
}
Bear in mind any nested objects may have 'required' and non required fields which means I either need to sanitize the response to match the response to how I want or do a check on each field to make sure it's not undefined.
If I bind to a grid component, it'll throw errors for undefined fields which may or may not be there.
Any ideas?
Seems like depending on the type of data you need a different way to display the data.
You could create a piece of state which is an array of Objects that hold info on how to handle each key.
import React from 'react'
import ReactDOM from 'react-dom'
class User extends React.Component {
constructor(props) {
super(props);
this.state = {
user_rows: [
{
key_name: "name",
data_function: (data = "") => data,
display:(data) => data.length > 0 ? true : false
},
{
key_name: "addresses",
data_function:(data = []) => data.map((a,idx) => {
return( <div>{a}</div>)
}),
display:(data) => data.length > 0 ? true : false
},
{
key_name: "gender",
data_function:(data = "") => data,
display:(data) => data.length > 0 ? true : false
}
]
}
}
render() {
let data = {
name: 'John',
surname: 'Doe',
addresses: ["adress 1"],
telephoneNumbers: ["000 000 000"],
gender: '',
age: 20
}
return (
<div>
{this.state.user_rows.map((ur,idx) => (
ur.display && <div key={idx}>{ur.data_function(data[ur.key_name])}</div>
))}
</div>
);
}
}
ReactDOM.render(
<User/>,
document.getElementById('container')
);

Make a common function to store the local storage data

I am a newbie in react-native. I have a folder structure like below:
-screens
-page1.js
-page2.js
-page3.js
-page4.js
-App.js
In page1.js, I have a function to store data to localStorage
let obj = {
name: 'John Doe',
email: 'test#email.com',
city: 'Singapore'
}
AsyncStorage.setItem('user', JSON.stringify(obj));
Now I have to display these data in few of my other pages. This is my code.
class Page2 extends Component {
state = {
username: false
};
async componentDidMount() {
const usernameGet = await AsyncStorage.getItem('user');
let parsed = JSON.parse(usernameGet);
if (parsed) {
this.setState({
username: parsed.name,
email: parsed.email
});
} else {
this.setState({
username: false,
email: false
});
}
}
render() {
return (
<View style={styles.container}>
<Text style={styles.saved}>
{this.state.username}
</Text>
</View>
);
}
}
export default Page2;
This is how I display data in page2. I may need to show these in other page too.
I dont want to repeat these codes in each page.
Any suggestions how to do it in react-native?
You can extract the data you need to display into it's own component and re-use it in any page that you need to display it in.
Another option is to use a higher-order component, that way you can wrap it around any components that need the data and it'll be passed down as a prop.
You can make your Constant.js where you can put all your common required utils and constants, reusable anywhere n your app.
In your Constant.js:
export const USER_DATA = {
set: ({ user}) => {
localStorage.setItem('user', JSON.stringify(obj));
},
remove: () => {
localStorage.removeItem('user');
localStorage.removeItem('refresh_token');
},
get: () => ({
user: localStorage.getItem('user'),
}),
}
in your any component, you can import it and use like this :
import { USER_DATA } from './Constants';
let user = {
name: 'John Doe',
email: 'test#email.com',
city: 'Singapore'
}
// set LocalStorage
USER_DATA.set(user);
// get LocalStorage
USER_DATA.get().user
That's you can make Constant common file and reuse them anywhere to avoid writing redundant code.
Simplified Reusable approach of localStorage
export const localData = {
add(key, value) {
localStorage.setItem(key, JSON.stringify(value));
},
remove(key, value) {
localStorage.removeItem(key);
},
load(key) {
const stored = localStorage.getItem(key);
return stored == null ? undefined : JSON.parse(stored);
},
};
localData.add("user_name", "serialCoder")
console.log( "After set 👉", localData.load("user_name") )
localData.remove("user_name")
console.log( "After remove 👉", localData.load("user_name") )

Redux state is being updated without dispatching any action

I should start off by saying this is not a duplicate of this question, which happened to have the same title.
I'm simply getting a customers object of arrays from props inside a componentDidMount method like this;
componentDidMount() {
const { customers } = this.props
const expiringCustomers = getExpiringCustomers(customers)
console.log('Expiring customers ', expiringCustomers)
}
Inside another file, I have that getExpiringCustomers function which takes the customers passed and is suppose to return a newly modified list of customers like this;
function numbersOnly(value) {
if(_.isString(value)) {
value = Number(value.replace(/[^\d]/g, ''))
}
return value
}
function normalizeNumber(collection, field) {
return collection.map(obj => {
obj[field] = numbersOnly(obj[field])
return obj
})
}
export function getExpiringCustomers(customers) {
const expiringCustomers = customers.filter(customer => {
const daysLeft = Number(new Date(customer.endDate)) - _.now()
if(daysLeft <= (dateInMonth * 3)) {
return customer
}
})
return normalizeNumber(expiringCustomers, 'rent')
}
I'm connecting my react component with redux state like this;
const mapStateToProps = state => ({
customers: state.customers.filter(customer => customer && !customer.deleted)
})
export default connect(mapStateToProps)(Accounting)
Problem
After the functions run and log results, customers' state is changed in redux store.
This is very confusing as customers_edit action has to pass through some procedures but none of them are called/logged.
Snapshot of the affected object:
Ps. The data is just boilerplate.
//- Focus on rent property
const customers = [
...,
{
id: 'o91wukyfsq36qidkld02a0voo93rna5w',
cardId: 'GD-1101010111',
id_type: 'Driving License',
firstName: 'Maalim',
lastName: 'Guruguja',
names: 'Maalim Guruguja',
property: '5iaprurefg3v3uhad688mypo9kqf6xk3',
rent: '250,000',
email: 'tonimarikapi#yahoo.com',
phone: '239-288-3838-38',
noticePeriod: '3',
status: '2 months remain',
startDate: '2018-07-09',
endDate: '2018-08-17',
createdAt: 1530623480772,
updatedAt: 1531213159147
},
...
]
//- After the functions run, log and edit customers array
const customers = [
...,
{
id: 'o91wukyfsq36qidkld02a0voo93rna5w',
cardId: 'GD-1101010111',
id_type: 'Driving License',
firstName: 'Maalim',
lastName: 'Guruguja',
names: 'Maalim Guruguja',
property: '5iaprurefg3v3uhad688mypo9kqf6xk3',
rent: 250000,
email: 'tonimarikapi#yahoo.com',
phone: '239-288-3838-38',
noticePeriod: '3',
status: '2 months remain',
startDate: '2018-07-09',
endDate: '2018-08-17',
createdAt: 1530623480772,
updatedAt: 1531213159147
},
...
]
From the linked question (possible duplicate one) the guy who answered stated that it's some mutation issue that may cause this. I'm not sure if that applies on props that are suppose to be read-only.
How can I stop these functions from updating my redux store, please help.
You mutate the objects in normalizeNumber, since all the array methods you use don't clone the array's objects.
Change normalizeNumber callback to return a new object with the updated field:
function normalizeNumber(collection, field) {
return collection.map(obj => ({
...obj,
[field]: numbersOnly(obj[field])
}))
}
It looks like you're modifying the customers array unintentionally.
Try:
componentDidMount() {
const { customers } = { ...this.props };
const expiringCustomers = getExpiringCustomers(customers)
console.log('Expiring customers ', expiringCustomers)
}

Apollo seems to refresh, when state is mapped to props, how can i prevent it?

I've build a component which basically list entries in a table, on top of that, i have another component to which filters can be applied. It all works really great with Apollo.
I'm trying to add deep linking into the filters, which on paper seems incredible simple, and i almost had i working.
Let me share some code.
const mapStateToProps = ({ activeObject }) => ({ activeObject });
#withRouter
#connect(mapStateToProps, null)
#graphql(FILTER_REPORT_TASKS_QUERY, {
name: 'filteredTasks',
options: (ownProps) => {
const filters = queryString.parse(location.search, { arrayFormat: 'string' });
return {
variables: {
...filters,
id: ownProps.match.params.reportId,
},
};
},
})
export default class TasksPage extends Component {
static propTypes = {
filteredTasks: PropTypes.object.isRequired,
activeObject: PropTypes.object.isRequired,
match: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
const filters = queryString.parse(location.search, { arrayFormat: 'string' });
this.state = { didSearch: false, initialFilters: filters };
this.applyFilter = this.applyFilter.bind(this);
}
applyFilter(values) {
const variables = { id: this.props.match.params.reportId };
variables.searchQuery = values.searchQuery === '' ? null : values.searchQuery;
variables.categoryId = values.categoryId === '0' ? null : values.categoryId;
variables.cardId = values.cardId === '0' ? null : values.cardId;
/*
this.props.history.push({
pathname: `${ this.props.history.location.pathname }`,
search: '',
});
return null;
*/
this.props.filteredTasks.refetch(variables);
this.setState({ didSearch: true });
}
..... Render functions.
Basically it calls the apply filter method, when a filter is chosen.
Which all works great, my problem is that when the activeObject is updated (By selecting a entry in the list). It seems to run my HOC graphql, which will apply the filters from the URL again, ignoring the filters chosen by the user.
I tried to remove the query strings from the url, once filters are applied, but i get some unexpected behavior, basically it's like it doesn't fetch again.
How can i prevent Apollo from fetching, just because the redux pushes new state?
I actually solved this by changing the order of the HOC's.
#graphql(FILTER_REPORT_TASKS_QUERY, {
name: 'filteredTasks',
options: (ownProps) => {
const filters = queryString.parse(location.search, { arrayFormat: 'string' });
return {
variables: {
...filters,
id: ownProps.match.params.reportId,
},
};
},
})
#withRouter
#connect(mapStateToProps, null)

Resources