I'm unable to understand why this happens
I'm using React to create a web app
I've a Javascript object called user_info
var user_info = {
username: '',
password: '',
f_name: ''
};
Now I want to assign these values to the ones that I fetch from my firebase Realtime Database.
db.ref("/users").on("value", snapshot => {
alert("Firebase " + snapshot.child("username").val()) // Got the value correctly.....
user_info.username = snapshot.child("username").val();
user_info.password = snapshot.child("password").val(); //Assigning it to the object...
user_info.f_name = snapshot.child("f-name").val();
alert("Firebase Username = " + user_info.username); //Assigned Successfully...
});
After this block of code (outside the snapshot function), I use the alert() to display the username again.
alert(user_info.username); // No value is displayed here.
I guess that the value from the snapshot is not assigned to the object user_info. Later I'm exporting this object and importing it in another file where I face the same problem.
export {user_info};
--- In the Other file ---------
import React from 'react';
import {user_info} from './users.js';
function LandingPage()
{
return(
<div className="container">
<h1>Welcome {user_info.username}</h1> // Only 'Welcome' is displayed
</div>
);
}
export default LandingPage;
I can't understand why the value is not assigned to the Object user_info. Please correct my code so that I could store the value in my object.
Thank You.
value is updated but component is not rerendered use state (useState hook) here then render it from state.
and use useEffect hooks for updating state when "user_info" is updated.
import React,{useState,useEffect} from 'react';
import {user_info} from './users.js';
function LandingPage()
{
const [userInfo,setUserInfo]=useState({});
useEffect(()=>{
setUserInfo(user_info);
},[user_info]);
return(
<div className="container">
<h1>Welcome {userInfo?.username}</h1> // Only 'Welcome' is displayed
</div>
);
}
export default LandingPage;
https://reactjs.org/docs/state-and-lifecycle.html
Related
I have a functional component that requires receiving parameter (i.e. props.rosterMonth) from the parent component and then retrieving data from the database, finally pass the result to the children component.
My problem is, how to set the initial state for the children component?
import {useEffect,useReducer} from 'react';
import Roster from '../../../utils/Roster';
import RosterWebContext from '../../../utils/RosterWebContext';
import Testing from './Testing';
export default function VVTable(props){
let dataReducer=(state,action)=>{
switch (action.type){
case 'updateRosterMonth':
return action.value;
default:return state;
}
}
const [contextValue, updateContext] = useReducer(dataReducer,{});
useEffect(()=>{
const getData = async () => {
console.log("Undo:Get Data from DB");
let roster = new Roster();
let rosterList= await roster.get(props.rosterMonth.getFullYear(),props.rosterMonth.getMonth()+1);
updateContext(
{
type:'updateRosterMonth',
value:{rosterList}
}
);
}
getData();
},[props]);
return(
<table id="rosterTable">
<RosterWebContext.Provider value={[contextValue, updateContext]}>
<Testing/>
</RosterWebContext.Provider>
</table>
)
}
This is the Testing component.
import {useContext,useEffect} from 'react';
import RosterWebContext from '../../../utils/RosterWebContext';
export default function Testing(props){
let [contextValue,updateContext]=useContext(RosterWebContext);
useEffect(()=>{
console.log(contextValue.rosterList);
},[contextValue])
return(
<tbody>
</tbody>
)
}
Currently, I set the initial state to an empty object, so the child component 'Testing' may receive an empty object, to prevent the undefined object error, I need to write some code to check the incoming context value. If I can set the database value to the initial state, I can remove those checking code.
The data from the database will always be delayed because it has to be retrieved via that API call from the server, (unless you are doing server side rendering). So you have to deal with the fact that at the beginning you don't have a rosterlist..
What I would do in this case is check if contextvalue.rosterList is defined and if it's not display a nice loading spinner to the user, something like this:
import {useContext,useEffect} from 'react';
import RosterWebContext from '../../../utils/RosterWebContext';
export default function Testing(props){
let [contextValue,updateContext]=useContext(RosterWebContext);
useEffect(()=>{
console.log(contextValue.rosterList);
},[contextValue])
return(
!!contextValue.rosterList?(
<tbody>
</tbody>
):(
<span>Loading Roster</span>
)
)
}
I have another js file that stores data like users.
export var selectedUser = null;
(the variable changes multiple times)
However, when I try accessing that data from App.vue file, it says it hasn't changed.
Here's how I'm importing the data:
import * as config from "./config"
If you import a regular JS file, it will not be reactive because regular JS do not have the VueJS state wired to it, hence it will not be reactive.
Why do you not add the state directly into VueJS ? Or even use Vuex.
You can make it reactive by creating a function to set selected user from the JavaScript file and return callback function if user selected and listen to callback in vuejs component to update the selectedUser variable (the variable will be in vuejs component)
Component code :
<template>
<div>
user {{ selectedUser }}
<button #click="update()"> Update user </button>
</div>
</template>
<script>
import getUserData from './GetUser';
import updateUser from './UpdateUser';
import {ref} from 'vue'
export default {
name: 'HelloWorld',
setup(){
let selectedUser = ref('ahmed');
const userData = getUserData();
function update(){
updateUser(selectedUser, (theNewValue) => {
// to be reactive
selectedUser.value = theNewValue;
});
}
return {...userData, update, selectedUser};
}
}
User file :
export default function updateUser(selectedUser, updated){
selectedUser = 'new value';
alert(`User Updated Successfully ${selectedUser}`);
// pass new value to get it from vuejs component
updated(selectedUser)
}
constant file -> constant.js
export default {
CITY: 'Banglore',
STATE: 'Karnataka'
}
Show Default City Name -> address.jsx
import React from "react";
import CONSTANTS from "./constants";
import "./styles.css";
const Address = () => {
return (
<div className="App">
<p> City : {`${CONSTANTS.CITY}`} </p>
<p> State : {`${CONSTANTS.STATE}`} </p>
</div>
);
};
export default Address;
expected output:
city: banglore
state: karnataka
we are importing the constant values from constant.js file, now the problem is we have to make one API call which may return overriding values for the constant keys
example of API response:
{
CITY: 'Mysuru'
}
then CITY is constant file should override with the new value which come after API response and rest other keys should keep their values same.
expected output:
city: Mysuru
state: karnataka
this the basic problem case for me, actually our application already in mid phase of development and more than 500+ constant keys are imported in 100+ components.
1. we are using redux in our application
2. we have to call API only once that should effects to all the components
what is the best way to achieve this problem, how can i override my constant files once i make the call to backend, Thank you
Since the question has changed, so does my answer (keeping the original one below). I'd suggest to rebuild the constants file to either return the constants or from Localstorage. However, be aware that the current components will not be rebuild using this approach. Only thing that'll trigger a rebuild is either use Redux for this or local state management.
const data = {
CITY: 'Banglore',
STATE: 'Karnataka'
}
const getData = () => {
let localData = window.localStorage.getItem('const-data');
if (!localData) {
axios.get('url')
.then(response => {
localData = {...response.data};
window.localStorage.setItem('const-data', JSON.stringify({...localData}));
});
}
return localData ? localData : data;
}
export default getData();
Original answer:
This is how I'd solve it using local state. It was some time ago since I was using Redux. Though the same principle should apply instead of putting the data in local state, put it in the Redux.
I prefer the simplicity of using local state whenever there's no need to share data over multiple components.
import React, { useEffect } from "react";
import CONSTANTS from "./constants";
import "./styles.css";
const Address = () => {
const [constants, setConstants] = useState({...CONSTANTS});
useEffect(() => {
//api call
//setConstants({...apiData});
}, []);
return (
<div className="App">
<p> City : {`${constants.CITY}`} </p>
<p> State : {`${constants.STATE}`} </p>
</div>
);
};
export default Address;
I have a simple react component which takes in a prop which is an Object.
I am trying to pass this in to a dispatch call to get it added to a list.
This works fine if I pass in a static value for the description field.
But fails when I use the dynamic field. I have checked via console log and the dynamic field definitely has a value and it is of type String as expected.
Why does it work when I use a static value (done solely for testing) for now but fails when I use the dynamic value?
Don't see any issues with my store, action, reducer setup cos it updates state fine with a static value.
Please advice. Thank you.
Experiencing this issue inside the ExpenseForm component existing within AddExpensePage where I
am using the expense value being passed in to perform the dispatch.
This is the working example with the static String value.
import React from "react";
import ExpenseForm from "./ExpenseForm";
import {connect} from "react-redux";
const AddExpensePage = (props) =>
<div>
<h1>Add Expense</h1>
<ExpenseForm onSubmit={(expense) => {
// This is what I want to do but unable to cos of the issue mentioned with description value thus breaking it out as follows.
// props.dispatch(addExpense(expense))
console.log(expense.description) // I do get a value like 'sample input' thus no issue with value
console.log(typeof expense.description) // it is of type String as expected
// breaking out the expense to be able to hard code value for description.
const tempFakeExpense = {
description: 'HARD_CODED_STRING_WHICH_WORKS', // expense.description will not update
note: expense.note,
amount: expense.amount,
createdAt: expense.createdAt
}
props.dispatch(addExpense(tempFakeExpense))
props.history.push('/');
}}/>
</div>
export default connect()(AddExpensePage);
The above doesn't work if I use the dynamic value as follows.
Meaning, if list had 10 items, it remains as 10. It doesn't append list to make it 11.
const actualExpense = {
description: expense.description, // not using hard coded value, thus not updating. Why?
note: expense.note,
amount: expense.amount,
createdAt: expense.createdAt
}
Expecting the issue to exist in above code. Adding the Expense component code below for reference in case there is some form of red herring.
There is no Redux in this component cos the state values are only for input tracking and not used anywhere else. Thus sticking to plain state/ setstate style of state management.
Note: The value got passed correctly to above component thus this state management works fine for ExpenseForm component and it doesn't need Redux to work in this component.
import React from 'react';
import moment from "moment";
import {SingleDatePicker} from "react-dates";
import 'react-dates/lib/css/_datepicker.css';
class ExpenseForm extends React.Component {
state = {
description: '',
note: '',
amount: '',
createdAt: moment(),
focused: false
}
render() {
return (
<div>
<form onSubmit={(e) => {
e.preventDefault();
if(this.state.description && this.state.amount) {
this.props.onSubmit({
description: this.state.description,
amount: parseFloat(this.state.amount),
note: this.state.note,
createdAt: this.state.createdAt.valueOf()
})
}
}}>
<input
type='text'
placeholder='description...'
autoFocus
value={this.state.description}
onChange={e => {
this.setState(() => ({
description: e.target.value
}))
}}
/>
{/* Setups for other fields */}
<button>Add Expense</button>
</form>
</div>
);
}
}
export default ExpenseForm;
I have the following code:
import React, { Component } from 'react';
import axios from 'axios';
class Dashboard extends Component {
state = {
name : 'randomname',
apiData: {}
};
componentDidMount() {
axios.get('https://api_url/getdata)
.then(res => {
const apiData = res.data
this.setState({apiData});
});
}
render() {
const { name, apiData} = this.state;
//THIS WORKS
var objTest = [{game_id: 2}]; //This is returned from apical
console.log(objTest[0].game_id);
console.log(apiData); //logs the following: [{game_id: 2}]
console.log(apiData[0]); //logs the following: {game_id: 2}
console.log(apiData[0].game_id); //Error occurs See error below
return (
<div className="wrap">
TESTING
</div>
);
}
}
export default Dashboard;
While testing and trying to get game_id using: console.log(apiData[0].game_id); I get the following error:
TypeError: undefined is not an object (evaluating
'apiData[0].game_id')
I would like to know why this works when I declare a variable and assign it the same values as the api call returns. But it does not work then I'm assigning the api call to apiData. It can only access apiData[0] which returns {game_id:2} , but cannot access apiData[0].game_id.
Thanks for all the help!
The main issue here is the order of life cycle methods. During mounting phase the constructor and then the render method is called. ComponentDidMount is not called yet and hence your state is empty. The reason you are not getting error when you log apiData or apiData[0] is it simply loggs empty array or object during initial render call (mounting phase) and then the actual object during the second render after componentDidMount(updateing phase). But when you try to call the property(game_id), you get an error(undefined) during the mounting phase since you are calling it on an empty array/object.
The solution is check for the existance of the parent object before calling the property on it , for example , usine optional chaining (JS2020 new future) which checks apiData[0], the error should be fixed just py appending "?" after the object. you can also use other methods for older JS.
console.log(apiData[0]?.game_id)
ComponentDidMount is triggered after the render method has loaded. Which means that console.log(apiData[0]) is calling the default state first before componentDidMount method is called.
Default state is an empty object here and not an array. So the index 0 of apiData is nothing. Changing the default state to apiData: [{game_id: null}] will give you the result and state will change once the componentDidMount is triggered and the api is successfully called.
This however is not the best approach. It's just to make things clear and understandable.
Simply defined one flag in the state and check whether your data is available or not once you get data change that flag and and load your component element accordingly.
see below solution for your problem statement.
import React, { Component } from 'react';
import axios from 'axios';
class Dashboard extends Component {
state = {
loading:true,
name : 'randomname',
apiData: {}
};
componentDidMount() {
axios.get('https://api_url/getdata').then(res => {
this.setState({apiData:res.data, loading:false});
});
}
render() {
const { name, apiData, loading} = this.state;
//THIS WORKS
var objTest = [{game_id: 2}]; //This is returned from apical
console.log(objTest[0].game_id);
console.log(apiData); //logs the following: [{game_id: 2}]
console.log(apiData[0]); //logs the following: {game_id: 2}
console.log(apiData[0].game_id); //Error occurs See error below
return (
<div className="wrap">
{loading ? <div>Loading ...</div> : <div className="wrap">TESTING</div>}
</div>
);
}
}
export default Dashboard;