The problem is that countryname ( separate from countryName) is scoped only to your function getData. Variables in JavaScript are scoped to the function/block they are defined within, depending on how you declare them (var vs let/const). In this case, countryName is available everywhere within your component, while countryname is only available within getData. This is a JavaScript-ism, and has nothing to do with you using React.
The problem is that countryname ( separate from countryName) is scoped only to your function getData. Variables in JavaScript are scoped to the function/block they are defined within, depending on how you declare them (var vs let/const). In this case, countryName is available everywhere within your component, while countryname is only available within getData. This is a JavaScript-ism, and has nothing to do with you using React.
First of all, setState ain't variable, but we need const to define state.
eg: const [isLoading, setIsLoading] = useState(true);
isLoading by default is true and mutable with setIsLoading.
Another example is const foo = 'bar';
In this scenario, foo is not able to change to other value.
Hope you understand.
React variables scope, defined how?
reactjs - React when to use global variables instead of states - Stack Overflow
React JS Access variable outside of scope - javascript
Reactjs - accessing variables - Stack Overflow
The reason why you use state instead of a variable outside the component is because of re-rendering.
If you do not use state, the component will not be updated with the new data returned from your api.
In addition, the correct way to use useEffect is as follows (commented), if you intend to update the data only once.
const [ cityList, setCityList ] = useState([]);
function Component(){
useEffects(()=>{ //<-- remove async here as it's not allowed.
const getData = async () => {
const data = await loadCities();
setCityList(data)
}
getData();
},[]); //<---- add a empty dependency so it only called once when component mounts
...
}
We usually use variable outside component if it doesn't change.
const initialData = ['a','b','c']
component A = () => {
// it's fine to use initialData out of the component because it doesn't change.
}
Of course, there are also times where we can use variable outside the component that can change over time. But only if the rendering of the component does not depend on it. (e.g. using a global variable to track a setTimeout)
Yes, don't use that method.
You can set a state to keep the values if they can change:
function Component() {
const [cityList, setCityList] = useState()
useEffect(async () => {
if (!cityList) {
const response = await loadCities();
setCityList(response);
}
}, [cityList]);
...
}
Check out this example from the React doc: https://reactjs.org/docs/faq-ajax.html#example-using-ajax-results-to-set-local-state
If the data don't change you can declare a variable inside the component:
function Component() {
const cityList = []
...
}
The hook useMemo that you have quoted is an optimization. It can be used to avoid unnecesary computations because it stores a memoized value.
useMemo is helpful when you have expensive calculations that depends on another values.
Check out the official doc about: https://reactjs.org/docs/hooks-reference.html#usememo
While the answer above is valid, it's always good to write modular code and keep your react components stateless (without having state variables). The following code shows a better approach to your problem:
import React from "react";
import listUnitItem from "./listUnitItem.scss";
//Number formatting
const numeral = require('numeral');
export default class ListUnitItem extends React.Component {
// Save this method in some Utils class where you can access from other components.
getFormattedNumber(number){
//format numbers greater than 1000
if (number > 1000) {
//only format number
number = numeral(number).format('0.0a');
}
return number
}
render() {
return(
<li className="list-unit-item col-sm-2">
<span className="unit-item-number">{this.getFormattedNumber(this.props.number)}</span>
<span className="unit-item-title">{this.props.title}</span>
</li>
)
}
}
I hope this helps!
I would personally put this into state using this.setState({}) in the componentWillMount function.
Something like
export default class ListUnitItem extends React.Component {
componentWillMount() {
let number = this.props.number;
if(number > 1000) {
number = numeral(number).format('0.0a');
}
this.setState({number})
}
render() {
return(
<li className="list-unit-item col-sm-2">
<span className="unit-item-number">{this.state.number}</span>
<span className="unit-item-title">{this.props.title}</span>
</li>
)
}
}
bvar declared inside your constructor is not accessible inside render() method. It is out of scope. As answered by Caleb, you would need to use instance variable: this.bvar = "cat"
When would I declare variables as:
a) state
Use state if changes in data should affect the view (e.g. store user location in state so that current temperature can be established and rendered based on this location). Also, state can be used in the logic found in other methods of your component (e.g. fetch background image based on user's current location).
b) between constructor() and render()
Variables declared inside other methods of your component are often used to temporarily store data coming, for example, from the state, props, input fields etc. These variables are only accessible within those methods, e.g.
constructor() {
...
}
onInputText() {
var accountNumber = this.refs.inputField.value;
this.props.handleInputText(accountNumber);
}
render() {
...
}
c) inside render()
Variables are often declared inside render() to temporarily store data held in state or props. These variables are only accessible inside render(), e.g.
class WelcomeScreen extends React.Component {
render() {
var userName = this.props.userName;
return (
<div>
Hello, { userName }!
</div>
);
}
}
To define bvar within the constructor you would need to declare it as
this.bvar = "cat"
Beyond React
You might not be aware that an import is global already. If you export an object (singleton) it is then globally accessible as an import statement and it can also be modified globally.
If you want to initialize something globally but ensure its only modified once, you can use this singleton approach that initially has modifiable properties but then you can use Object.freeze after its first use to ensure its immutable in your init scenario.
const myInitObject = {}
export default myInitObject
then in your init method referencing it:
import myInitObject from './myInitObject'
myInitObject.someProp = 'i am about to get cold'
Object.freeze(myInitObject)
The myInitObject will still be global as it can be referenced anywhere as an import but will remain frozen and throw if anyone attempts to modify it.
Example of react state using singleton
https://codesandbox.io/s/adoring-architecture-ru3vt (see UserContext.tsx)
If using react-create-app
(what I was looking for actually) In this scenario you can also initialize global objects cleanly when referencing environment variables.
Creating a .env file at the root of your project with prefixed REACT_APP_ variables inside does quite nicely. You can reference within your JS and JSX process.env.REACT_APP_SOME_VAR as you need AND it's immutable by design.
This avoids having to set window.myVar = %REACT_APP_MY_VAR% in HTML.
See more useful details about this from Facebook directly:
https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables
Why don't you try using Context?
You can declare a global context variable in any of the parent components and this variable will be accessible across the component tree by this.context.varname. You only have to specify childContextTypes and getChildContext in the parent component and thereafter you can use/modify this from any component by just specifying contextTypes in the child component.
However, please take a note of this as mentioned in docs:
Just as global variables are best avoided when writing clear code, you should avoid using context in most cases. In particular, think twice before using it to "save typing" and using it instead of passing explicit props.