React - override state by exists object properties - reactjs

I have state with properties initialized with empty string, because im putting these into form inputs. Additionaly I get object from Firebase with properties matching with state but not every one exists. I would set state properties by properties of this object if properties exists.
Here is necessary example:
class App extends Component {
state = {
user: {
firstName: "",
lastName: "",
gender: "",
address: {
street: "",
city: "",
state: ""
}
}
}
componentDidMount(){
const newObjectFromFirebase = {
firstName: "Lucas",
address: {
city: "London"
}
}
this.setState(newObjectFromFirebase);
}
render() {
const stateJson = JSON.stringify(this.state);
return (
<div className="App">
{stateJson}
</div>
);
}
}
I would like to get state with override properties in only these properties which exists in object from firebase. Otherwise I would like to keep the previous state. Currently state properties are completely overwritten by new object.
Thats what I want:
user: {
firstName: "Lucas",
lastName: "",
gender: "",
address: {
street: "London",
city: "",
state: ""
}
}
Thats what I have:
user: {
firstName: "Lucas",
address: {
city: "London",
}
}
Any smart solution or library ?

You can just use the spread syntax ... like this:
const newObjectFromFirebase = {
firstName: "Lucas",
address: {
city: "London"
}
}
this.setState(prevState => {
user: {
...prevState.user,
...newObjectFromFirebase,
address: {
...prevState.user.address,
...newObjectFromFirebase.address
}
}
});
This will merge the 2 objects together and override the state.

Related

Setting Values for State in React

Good Afternoon,
I am having an issue setting an object via useState and I really do not understand why. Below is the input that I am using to display the information from the database based on the ?: statement. I am unsure why the state will not update, and it is possible that I am not handling the state properly. Can someone help me understand what I am doing wrong?
const [userUpdate, setUserUpdate] = useState({
firstName: "",
lastName: "",
email: "",
dob: "",
gender: "",
address: {
billing: {
address1: "",
address2: "",
city: "",
state: "",
postalCode: "",
},
shipping: {
address1: "",
address2: "",
city: "",
state: "",
postalCode: "",
},
},
});
<FormGroup widths="equal" id="billing">
<FormField
id="address1"
control={Input}
label="Address Line 1"
placeholder="123 Main Street"
defaultValue={
userUpdate.address.billing.address1
? userUpdate.address.billing.address1
: user.address.billing.address1
}
onBlur={(e) => {
handleChange("b", e);
}}
/>
</FormGroup>
The handleChange -> b is the section of the address that I need to update.. Billing or Shipping etc.
const handleChange = (section, e) => {
const form = e.target.form.id;
if (form === "user") {
console.log(section);
if (section === "b") {
setUserUpdate({
...userUpdate,
address: {
billing: {
[e.target.id]: e.target.value.trim(),
},
},
});
} else if (section === "s") {
setUserUpdate({
...userUpdate,
address: {
shipping: {
[e.target.id]: e.target.value.trim(),
},
},
});
} else {
setUserUpdate({
...userUpdate,
[e.target.id]: e.target.value.trim(),
});
}
}
if (form === "category") {
setCategoryUpdate({
...categoryUpdate,
[e.target.id]: e.target.value.trim(),
});
}
};
console log is showing the correct e.target.id => address1 and e.target.value.trim() => address, but its not updating the state..
now my code is throwing an error on the form default value
×
TypeError: Cannot read properties of undefined (reading 'address1')
GenerateActiveScreen
C:/Development/Picwrist Web Application/client/src/components/screens/user/Dashboard.js:397
394 | control={Input}
395 | label="Address Line 1"
396 | placeholder="123 Main Street"
> 397 | defaultValue={
| ^ 398 | userUpdate.address.shipping.address1
399 | ? userUpdate.address.shipping.address1
400 | : user.address.shipping.address1
Changed ID's for the fields and updated the handleChange state to pass the existing values from the previous state.
const handleChange = (section, e) => {
const form = e.target.form.id;
if (form === "user") {
const id = e.target.id;
const value = e.target.value.trim();
if (section === "b") {
setUserUpdate((userUpdate) => ({
...userUpdate,
address: {
...userUpdate.address,
billing: {
...userUpdate.address.billing,
[id.slice(1)]: value,
},
},
}));
} else if (section === "s") {
setUserUpdate((userUpdate) => ({
...userUpdate,
address: {
...userUpdate.address,
shipping: {
...userUpdate.address.shipping,
[id.slice(1)]: value,
},
},
}));
} else {
setUserUpdate({
...userUpdate,
[e.target.id]: e.target.value.trim(),
});
}
}
if (form === "category") {
setCategoryUpdate({
...categoryUpdate,
[e.target.id]: e.target.value.trim(),
});
}
};

`getDerivedStateFromProps` is not updating the state

I am trying to update the state of class component via props that sent from above component,
At first I've set an constructor that fetch the data from the props and place it in the state but that did not work because of the render() did not called.
So I've reed of the function getDerivedStateFromProps which called before render, But I don't know if I'm using it correct.
I'm send an prop as institute from above component and implement the getDerivedStateFromProps() like this:
static getDerivedStateFromProps(nextProps, prevState) {
if (prevState.id !== nextProps.institute.id) { // need to update
return {
id: nextProps.id,
name: nextProps.name,
contactName: nextProps.contactName,
phoneNumber: nextProps.phoneNumber,
city: nextProps.city,
street: nextProps.street,
number: nextProps.number,
deleted: nextProps.deleted
};
}
console.log("null")
return null;
}
I've also set an initState values:
state = {
id: '',
name: '',
contactName: '',
phoneNumber: '',
city: '',
street: '',
number: '',
deleted: '',
}
getDerivedStateFromProps if statement is true but it's not updating the state at all.
I will appreciate any kind of help.
Thanks!

How to pass props of a nested object to a component?

I am having a little hard time figuring out this issue and would appreciate if someone clear this concept to me. With the following code, I can parse props to another component i.e. UserItem and it displays the username and email. I also want to display "firstname" along with other details. I can easily destructure others like: const {username, email} = this.state.user but when I add const {name: fisrtname, username, email}, then it doesn't work. But if I put this.state.user.name.firstname in the UserItem component, then I can see the firstname in the browser. So how I will destructure the nested object and/or parse the props of nested object to other component? Thanks in advance.
users: [
{
username: "johndoe",
email: "jdoe#gmail.com",
name: {
firstname: "John",
id: "2",
},
id: "1",
},
],
};
render() {
return (
<div>
{this.state.users.map((user) => (
<UserItem key={user.id} user={user} />
))}
</div>
);
}
}
**This is UserItem component**
class UserItem extends Component {
render() {
const { name: firstname, email, username, phone } = this.props.user;
// console.log(this.props.user);
return (
<div>
<h3>{this.props.user.name.firstname}</h3>
<h3>{email}</h3>
<h3>{username}</h3>
<h3>{phone}</h3>
</div>
);
}
}
I am just extending and describing the #Alex Yepes's Answer.
Look at your code
{
username: "johndoe",
email: "jdoe#gmail.com",
name: { // here the name is also an object.
firstname: "John",
id: "2",
},
id: "1",
},
Since 'name' is also an object you need to destructure that one too.
For example:
On your first destructure, you can only access directly the property of that object
const {username, email, name, id} = user;
console.log(username); //result: 'johndoe'
console.log(name); //result: {firstname: 'John', id: 2}
Here you can see that username's value is a string but the value of 'name' is an object that's why you need to destructure this one too. Other wise you have to use object or bracket notation like:
console.log(name.firstname); // result: 'John'
//or
console.log(name[firstname]) // result: 'John'
But if you destructure like this one:
const { name: {firstname}, email, username, phone } = this.props.user;
you can access 'name' directly because the 'name' object is not an object anymore.
This:
const { name: firstname, email, username, phone } = this.props.user;
needs to be changed to this:
const { name: {firstname}, email, username, phone } = this.props.user;
That way you can access the nested property.
you need the constructor in the object above the render.
class UserItem extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {

Migration to Mobx 6: functional components aren't working with decorated observables

I faced with problem while migrating from Mobx 4 to Mobx 6.
I have a functional component but after updating Mobx it stopped working. Looks like store doesn't works. Component react on changes inside observable variable by reaction feature but changes aren't re-rendering. I made everything that was provided in migration guide but component's store doesn't working.
At some reason if I change functional component to class component everything starts working. But I really can't understand the reason why such happens and can't find any explanation of such behaviour.
Case looks like example bellow. Experimental decorators are enabled and any other stuff that was provided in Migration guide as well. So what is the reason of such behaviour and how can I implement correct logic in functional component?
interface User {
name: string;
age: number;
info: {
phone: string;
email: string;
};
}
const usersData: User[] = [
{
name: "Steve",
age: 29,
info: {
phone: "+79011054333",
email: "steve1991#gmail.com",
},
},
{
name: "George",
age: 34,
info: {
phone: "+79283030322",
email: "george_the_best_777#gmail.com",
},
},
{
name: "Roger",
age: 17,
info: {
phone: "+79034451202",
email: "rodge_pirat_yohoho#gmail.com",
},
},
{
name: "Maria",
age: 22,
info: {
phone: "+79020114849",
email: "bunnyrabbit013#gmail.com",
},
},
];
const getUsers = () => {
return new Promise<User[]>((resolve) => {
setTimeout(() => {
resolve(usersData);
}, 2000);
});
};
class Store {
#observable users: User[] = [];
constructor() {
makeObservable(this);
}
async init() {
const users = await getUsers();
this.setUsers(users);
}
#action setUsers(users: User[]) {
this.users = users;
}
#action increaseUserAge(userIndex: number) {
const users = this.users.map((u, k) => {
if (k === userIndex) {
u.age += 1;
}
return u;
});
this.setUsers(users);
}
#computed get usersCount(): number {
return this.users.length;
}
}
const store = new Store();
const UserList = observer(() => {
React.useEffect(() => {
store.init();
}, []);
const addOneUser = () => {
const user = {
name: "Jesica",
age: 18,
info: {
phone: "+79886492224",
email: "jes3331#gmail.com",
},
};
store.setUsers([...store.users, user]);
};
return (
<div className="App">
<h4>Users: {store.usersCount}</h4>
{store.users.length ? (
<>
<ul>
{store.users.map((user, key) => (
<li key={key}>
Name: {user.name}, Age: {user.age}, Info:
<div>
Phone: {user.info.phone}, Email: {user.info.email}
</div>
<button onClick={() => store.increaseUserAge(key)}>
Increase Age
</button>
</li>
))}
</ul>
<button onClick={addOneUser} disabled={store.usersCount >= 5}>
Add one user
</button>
</>
) : (
<p>Fetching users...</p>
)}
</div>
);
});
function App() {
return <UserList />;
}
export default App;
I've made Codesandbox example with your code (although removed types), it works fine.
Check tsconfig.json there, maybe you forgot to enable some of the options?
Or check what versions of mobx and mobx-react are you using?
And just a small nitpick on how you use your increaseUserAge action, it can be as simple as that:
#action increaseUserAge(user) {
user.age += 1;
}
And in the jsx you just pass the whole user there:
<button onClick={() => store.increaseUserAge(user)}>
Increase Age
</button>

Why is this not rendering from a map, in react

So I get items from a DB and console.log them just before, so I know they exist and are in the format I want them to be. I also wrote a simple method to make sure rendering works. The code looks something like this (is simplified for nonrelevantbits)
const renderSomething = () => {
getFromDB().then(function(data){
if (data) {
console.log(data.something) //returns an array in console
return data.something.map(somet => {
console.log(' somet is ' + somet) //return somet with quotes: ""
return <p>{somet}</p>
})
} else {
console.log("No data!");
}
}
}
and then in the return:
return (
<div>
{renderSomething()}
</div>
)
And nothing appears on the screen. I made a test one to make sure it should:
const basic = () => {
return ['abc', 'bca'].map(num => <h1>{num}</h1>)
}
return (
<div>
{basic()}
</div>
)
The test one worked
Render function cannot be a promise. You should useEffect method to downlaod the data from api and then useState to set it for the component.
import React, { Fragment, useState, useEffect } from "react";
import ReactDOM from "react-dom";
function LiveVisitors() {
const [visitors, setVisitors] = useState([
{
ip: "",
countryCode: "",
city: "Warsaw",
state: "",
country: "Poland"
},
{
ip: "",
countryCode: "",
city: "Gdańsk",
state: "",
country: "Poland"
}
]);
const { ip, countryCode, city, state, country } = visitors;
useEffect(() => {
getUserData();
}, []);
const getUserData = () => {
//imitate fetch
setVisitors([
...visitors,
{
ip: "",
countryCode: "",
city: "Wrocław",
state: "",
country: "Poland"
}
]);
};
return (
<div>
{visitors.map((el) => (
<div>{el.city}</div>
))}
</div>
);
}
const wrapper = document.getElementById("container");
ReactDOM.render(<LiveVisitors />, wrapper);
Here is the interactive version where you can play with it.
https://codesandbox.io/s/react-playground-forked-hqswi?file=/index.js:0-947

Resources