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;
Related
Problem
When i change the tag value it only changes on the select component but not in the index.astro
I have folder signals where i export signal
export const tagSignal = signal<string>("all");
I use it like this in Select.tsx component, and here evryting changes
import { tagSignal } from "#signal/*";
const setTagValue = (value: string) => {
tagSignal.value = value;
console.log("select", tagSignal.value);
};
export const Select = () => {
const [display, setDisplay] = useState(false);
const [selectedName, setSelectedName] = useState("all"); // this will be change to only signals still under refator
setTagValue(selectedName);
-------
------
but when I import it to index.astro like this I get only "all" value witch is inital value
---
import { Icon } from "astro-icon";
import { Picture } from "astro-imagetools/components";
import Layout from "#layouts/Layout.astro";
import { Select } from "#components/Select";
import Card from "#components/Card.astro";
import { getCollection } from "astro:content";
import { getProjectsByTag } from "#utils/*";
import { tagSignal } from "#signal/*";
const projects = await getCollection("projects");
const filteredProjects = getProjectsByTag(projects, tagSignal.value);
// TODO: add links
console.log("index", tagSignal.value);
---
/// some code here
<section id="projects" class="projects">
<Select client:only="preact" />
<div class="projects-wrapper">
{
filteredProjects.map(({ data: { title, heroImage } }) => (
<Card name={title} bg_path={heroImage} />
))
}
</div>
</section>
---
I see two issues here.
You are depending on dynamic JS variables in an .astro file. It doesn't work the way you are expecting—all the javascript in .astro files, with the exception of the "islands," e.g., your Select.tsx component, is being evaluated when the page is being built. So Astro grabs the initial value of tagSignal, but makes it a static string.
People can get bitten by, e.g., the trivial ©2010—{new Date().getFullYear()} in the footer – it won't magically update on the new year's eve if used in .astro file.
The state (signal's current value) is not shared accross the islands. If you want to share it, you need either a global state solution (I haven't used it in Astro yet), or just create a common parent for the Select and the filtering logic, e.g.:
{/* the signal will be declared (or imported) in `DynamicSection`*/}
<DynamicSection client:only="preact">
<Select />
<div class="projects-wrapper">
{
filteredProjects.map(({ data: { title, heroImage } }) => (
<Card name={title} bg_path={heroImage} />
))
}
</div>
</ DynamicSection>
(The simplest global state solution would be probably using the url with a query string, and derive the state from its value).
I am trying to create a custom widget for Netlify CMS that connects to the redux store to be updated when a file widget has a new path. My widget will then determine the size of the file to set it's own value.
So far, I have simply cloned the starter widget and changed src/Conrol.js to this:
import React from 'react';
import { connect } from 'react-redux' //Added this line
class Control extends React.Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
forID: PropTypes.string,
value: PropTypes.node,
classNameWrapper: PropTypes.string.isRequired,
}
static defaultProps = {
value: '',
}
render() {
const {
forID,
value,
onChange,
classNameWrapper,
} = this.props;
return (
<input
type="text"
id={forID}
className={classNameWrapper}
value={value || ''}
onChange={e => onChange(e.target.value)}
/>
);
}
}
//Added everything below
const mapStateToProps = (state, ownProps) => {
const fileUrl = "test";
return { value: fileUrl };
};
export default connect(mapStateToProps, null, null, { withRef: true })(Control);
With these changes I get the following error:
Element ref was specified as a string (wrappedInstance) but no owner
was set. This could happen for one of the following reasons: 1. You
may be adding a ref to a functional component 2. You may be adding a
ref to a component that was not created inside a component's render
method 3. You have multiple copies of React loaded See
https://reactjs.org/link/refs-must-have-owner for more information.
I can't figure out what I'm doing wrong. As for the reasons given in the error:
I'm working with a class component.
I didn't even touch the component rendering. I only added the connect().
I ran npm ls react as suggested at the link provided in the error. I got two copies of react, but one was "deduped".
I have searched far and wide for an answer, but have not found one. I am new to react/redux, so I'm hoping someone can point me in the right direction.
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
I'm trying to use the new React context to hold data about the logged-in user.
To do that, I create a context in a file called LoggedUserContext.js:
import React from 'react';
export const LoggedUserContext = React.createContext(
);
And sure enough, now I can get access to said context in other components using consumers, as I do here for example:
<LoggedUserContext.Consumer>
{user => (
(LoggedUserContext.name) ? LoggedUserContext.name : 'Choose a user or create one';
)}
</LoggedUserContext.Consumer>
But obviously, for this system to be useful I need to modify my context after login, so it can hold the user's data. I'm making a call to a REST API using axios, and I need to assign the retrieved data to my context:
axios.get(`${SERVER_URL}/users/${this.state.id}`).then(response => { /*What should I do here?*/});
I see no way to do that in React's documentation, but they even mention that holding info of a logged in user is one of the use cases they had in mind for contexts:
Context is designed to share data that can be considered “global” for
a tree of React components, such as the current authenticated user,
theme, or preferred language. For example, in the code below we
manually thread through a “theme” prop in order to style the Button
component:
So how can I do it?
In order to use Context, you need a Provider which takes a value, and that value could come from the state of the component and be updated
for instance
class App extends React.Component {
state = {
isAuth: false;
}
componentDidMount() {
APIcall().then((res) => { this.setState({isAuth: res}) // update isAuth })
}
render() {
<LoggedUserContext.Provider value={this.state.isAuth}>
<Child />
</LoggedUserContext.Provider>
}
}
The section about dynamic context explains it
Wrap your consuming component in a provider component:
import React from 'react';
const SERVER_URL = 'http://some_url.com';
const LoggedUserContext = React.createContext();
class App extends React.Component {
state = {
user: null,
id: 123
}
componentDidMount() {
axios.get(`${SERVER_URL}/users/${this.state.id}`).then(response => {
const user = response.data.user; // I can only guess here
this.setState({user});
});
}
render() {
return (
<LoggedUserContext.Provider value={this.state.user}>
<LoggedUserContext.Consumer>
{user => (
(user.name) ? user.name : 'Choose a user or create one';
)}
</LoggedUserContext.Consumer>
</LoggedUserContext.Provider>
);
}
}
I gave a complete example to make it even clearer (untested). See the docs for an example with better component composition.
I've been getting started with react-redux and finding it a very interesting way to simplify the front end code for an application using many objects that it acquires from a back end service where the objects need to be updated on the front end in approximately real time.
Using a container class largely automates the watching (which updates the objects in the store when they change). Here's an example:
const MethodListContainer = React.createClass({
render(){
return <MethodList {...this.props} />},
componentDidMount(){
this.fetchAndWatch('/list/method')},
componentWillUnmount(){
if (isFunction(this._unwatch)) this._unwatch()},
fetchAndWatch(oId){
this.props.fetchObject(oId).then((obj) => {
this._unwatch = this.props.watchObject(oId);
return obj})}});
In trying to supply the rest of the application with as simple and clear separation as possible, I tried to supply an alternative 'connect' which would automatically supply an appropriate container thus:
const connect = (mapStateToProps, watchObjectId) => (component) => {
const ContainerComponent = React.createClass({
render(){
return <component {...this.props} />
},
componentDidMount(){
this.fetchAndWatch()},
componentWillUnmount(){
if (isFunction(this._unwatch)) this._unwatch()},
fetchAndWatch(){
this.props.fetchObject(watchObjectId).then((obj) => {
this._unwatch = this.props.watchObject(watchObjectId);
return obj})}
});
return reduxConnect(mapStateToProps, actions)(ContainerComponent)
};
This is then used thus:
module.exports = connect(mapStateToProps, '/list/method')(MethodList)
However, component does not get rendered. The container is rendered except that the component does not get instantiated or rendered. The component renders (and updates) as expected if I don't pass it as a parameter and reference it directly instead.
No errors or warnings are generated.
What am I doing wrong?
This is my workaround rather than an explanation for the error:
In connect_obj.js:
"use strict";
import React from 'react';
import {connect} from 'react-redux';
import {actions} from 'redux/main';
import {gets} from 'redux/main';
import {isFunction, omit} from 'lodash';
/*
A connected wrapper that expects an oId property for an object it can get in the store.
It fetches the object and places it on the 'obj' property for its children (this prop will start as null
because the fetch is async). It also ensures that the object is watched while the children are mounted.
*/
const mapStateToProps = (state, ownProps) => ({obj: gets.getObject(state, ownProps.oId)});
function connectObj(Wrapped){
const HOC = React.createClass({
render(){
return <Wrapped {...this.props} />
},
componentDidMount(){
this.fetchAndWatch()},
componentWillUnmount(){
if (isFunction(this._unwatch)) this._unwatch()},
fetchAndWatch(){
const {fetchObject, watchObject, oId} = this.props;
fetchObject(oId).then((obj) => {
this._unwatch = watchObject(oId);
return obj})}});
return connect(mapStateToProps, actions)(HOC)}
export default connectObj;
Then I can use it anywhere thus:
"use strict";
import React from 'react';
import connectObj from 'redux/connect_obj';
const Method = connectObj(React.createClass({
render(){
const {obj, oId} = this.props;
return (obj) ? <p>{obj.id}: {obj.name}/{obj.function}</p> : <p>Fetching {oId}</p>}}));
So connectObj achieves my goal of creating a project wide replacement for setting up the connect explicitly along with a container component to watch/unwatch the objects. This saves quite a lot of boiler plate and gives us a single place to maintain the setup and connection of the store to the components whose job is just to present the objects that may change over time (through updates from the service).
I still don't understand why my first attempt does not work and this workaround does not support injecting other state props (as all the actions are available there is no need to worry about the dispatches).
Try using a different variable name for the component parameter.
const connect = (mapStateToProps, watchObjectId) => (MyComponent) => {
const ContainerComponent = React.createClass({
render() {
return <MyComponent {...this.props} obj={this.state.obj} />
}
...
fetchAndWatch() {
fetchObject(watchObjectId).then(obj => {
this._unwatch = watchObject(watchObjectId);
this.setState({obj});
})
}
});
...
}
I think the problem might be because the component is in lower case (<component {...this.props} />). JSX treats lowercase elements as DOM element and capitalized as React element.
Edit:
If you need to access the obj data, you'll have to pass it as props to the component. Updated the code snippet