I am following Getting started with React Native and Firebase tutorial on medium.
I came across below code.
constructor(props) {
super(props);
Firebase.initialise();
this.getInitialView();
this.state = {
userLoaded: false,
initialView: null
};
//What this code does?
this.getInitialView = this.getInitialView.bind(this);
}
getInitialView() {
firebase.auth().onAuthStateChanged((user) => {
let initialView = user ? "Home" : "Login";
this.setState({
userLoaded: true,
initialView: initialView
})
});
}
can some body explain below statement? when and why should i use it? and what are all the advantages?
this.getInitialView = this.getInitialView.bind(this);
Basically anytime you want to use this, you want it to be referring to the component. ES6 Classes don't autobind, so there are multiple ways to handle this issue:
You can bind in the constructor like in your example.
You can use an arrow function wherever you call your method: ( ) => this.getInitialView( ).
You can bring in a library to autobind the methods for you.
I do it this way: When defining the methods, do it like this, since arrow functions do not define a scope of their own:
getInitialView = () => {
firebase.auth().onAuthStateChanged((user) => {
let initialView = user ? "Home" : "Login";
this.setState({
userLoaded: true,
initialView: initialView
})
});
}
It bind the this keyword to the react component. Otherwise you couldn't use this inside the method.
The difference is without the line you question this would refer to something else and not have setState as a function which is important to react components.
It does appear though, given the context, that it's not used because the only usage of this is in another function it should change the context. I'd think you'd need to do to achieve the desired results. (code snippet removed)
Related
I have 3 functional components and I'd like to pass & manipulate data through useRef.
I am adding this onClose method to current property in one of my component.
const onClose = () => {
setButtonColor(buttonColor);
};
ref.current = {
clearSwitchStateOnClose: onClose,
};
I am calling that method in another component.
ref.current.clearSwitchStateOnClose();
I am receiving this error,
Uncaught TypeError: ref.current.clearSwitchStateOnClose is not a function
I was calling this method ref.current.clearSwitchStateOnClose(); but with that I was also mutating ref. I guess that was causing the issue.
It was something like this,
ref.current = {
showModal: false,
modalResponse: null,
};
ref.current.clearSwitchStateOnClose();
After doing this, it is working fine.
ref.current.clearSwitchStateOnClose();
ref.current = {
showModal: false,
modalResponse: null,
};
Sorry for not sharing the entire scenario of this situation.
So I've always thought of arrow functions to be a new better and version of normal js functions until today. I was following a tutorial on how to use firestore to store data when I came across a problem that made realise the two are different and work in a weird way.
His code looked like this:
//component
function Todos() {
const [ todo, setTodo ] = useState('');
const ref = firestore().collection('todos');
// ...
async function addTodo() {
await ref.add({ title: todo, complete: false});
setTodo('');
}
// ...
}
My code looked like this:
//component
const Todos = () => {
const ref = firestore().collection('todos');
const [todo, setTodo] = useState('');
const addTodo = async () => {
const res = await ref.add({ title: todos, complete: false });
setTodo('');
};
};
Now his version worked, while mine didn't.
After changing my code to look like his, it worked. But the weird thing i realised was this: after clicking on the button that invoked that function for the first time (with his function), i changed the code back to mine and it worked the second time. I did some reading on the two functions but i couldn't get to reasoning behind why this happened.
Arrow functions and normal function are not equivalent.
Here is the difference:
Arrow function do not have their own binding of this, so your this.setState refer to the YourClass.setState.
Using normal function, you need to bind it to the class to obtain Class's this reference. So when you call this.setState actually it refer to YourFunction.setState().
Sample Code
class FancyComponent extends Component {
handleChange(event) {
this.setState({ event }) // `this` is instance of handleChange
}
handleChange = (event) => {
this.setState({ event }) // `this` is instance of FancyComponent
}
}
I'm trying to update to update the window state whenever the App component mounts. With the below code, I receive an Error in response to tabs.query: TypeError: this.addTabs is not a function.
I don't understand why this.addTabs is not considered a function, as the function is above the reference to this.addTabs(tabs), and I think it was correctly bound.
class App extends Component {
constructor(props){
super(props);
this.state = {
window: []
};
this.addTabs = this.addTabs.bind(this);
}
addTabs(tabs){
this.setState({window:this.state.window.concat(tabs)});
};
componentDidMount(){
chrome.tabs.query({active:true},function(tabs){
this.addTabs(tabs);
});
I'm not looking to use the arrow function. I looked at similar questions, and the response was to bind the function in the constructor, which I believe I did. Any help or pointers would be appreciated!
Your issue is in this block:
componentDidMount(){
chrome.tabs.query({active:true},function(tabs){
this.addTabs(tabs);
});
}
Inside your callback, the context is different so this refers to another context.
You have several ways to fix it.
1) Assign the ref to this outside the callback and use that ref:
componentDidMount(){
const that = this;
chrome.tabs.query({active:true},function(tabs){
that.addTabs(tabs);
});
}
2) Bind the current this to the callback:
componentDidMount(){
chrome.tabs.query({active:true},(function(tabs){
this.addTabs(tabs);
}).bind(this));
}
3) Use an arrow function:
componentDidMount(){
chrome.tabs.query({active:true}, (tabs) => {
this.addTabs(tabs);
});
}
I am working in a small application for a class I am taking and I have an issue when I am using the fetch API
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
map: "",
markers: [],
Data: []
};
}
componentDidMount() {
fetch(
`https://api.foursquare.com/v2/venues/explore?near=ashkelon&v=20180729&client_id=MVLZGLPIAITJITM0OOFYER3C2ZRT5ERGGEWCC0T1YWV3HFZA&client_secret=1TBLTY0TSM1T320FEO3BJBGTMYVQPCMBOGO5GEBC0ZB1E5LK`
)
.then(function(response) {
return response.json();
})
.then(
function(data) {
this.setState({ Data: data });
}.bind(this)
)
.catch(function(e) {
console.log("There is an issue with getting the information", e);
});
}
}
window.initMap = this.initMap;
loadJS("https://maps.googleapis.com/maps/api/js?key=AIzaSyDySeqpuOjQlckWJUMlbSW_w5CydOVTWJI&callback=initMap");
UPDATE :
this will not provide with an error and the state is set, but what now happens is that my state is empty when i log the state in the initMap method.
At this point i see that the state is set for "that".
But if its set for "that" how can i use "this" state in the rest of my application i need this information to create markers on the google maps API
thanks in advance.
The problem is that this is undefined in your anonymous function. By assigning const that = this, you make the context from componentDidMount() available in all of the anonymous functions. Another solution is to bind() all functions with the correct context. For example
...
.then((function(data) {
this.setState({Data: data.response.groups[0]})
console.log(this.state.Data);
}).bind(this))
...
Now you can remove the declaration for that.
If you don't really care about IE11 support (and does not use Babel) please consider using arrow functions (it's syntax sugar for functions that have the same this as the this around them)
Note that string literals like you used, have similar compatibility table as arrow functions so you lose nothing and gain much cleaner code!
The code with arrow functions looks like this:
componentDidMount() {
/* ... */
fetch(`URL`)
.then(response => response.json())
.then(data => this.setState({Data: data.response.groups[0]}))
.catch(e => console.log('There is an issue with getting the information' , e))
/* ... */
}
After reading official react.js documentation I understand how it should work in a good way, like
I have list of items in initial component state
adding new item through setState will update state and trigger update of UI
What should I do if I use external object as model like some global array which should be available for some not react.js parts of code OR could be modified with web sockets somewhere in future? Is calling ReactDOM.render after each action a good way? AFAIK it should work ok from performance point of view.
You still use setState:
let React = require('React');
let externalThing = require('tools/vendor/whoever/external-lib');
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = this.getInitialState();
}
getInitialState() {
// This assumes your external thing is written by someone who was
// smart enough to not allow direct manipulation (because JS has
// no way to monitor primitives for changes), and made sure
// to offer API functions that allow for event handling etc.
externalThing.registerChangeListener(() => this.updateBasedOnChanges(externalThing));
return { data: externalThing.data }
}
updateBasedOnChanges(externalThing) {
// note that setState does NOT automatically trigger render(),
// because React is smarter than that. It will only trigger
// render() if it sees that this new 'data' is different
// (either by being a different thing entirely, or having
// different content)
this.setState({
data: externalThing.data
});
}
render() {
// ...
}
}
If the external thing you're using is terribly written and you have to manipulate its data directly, your first step is to write an API for it so you don't directly manipulate that data.
let externalData = require('externaldata') // example: this is a shared array
let ExternalDataAPI = new ExternalDataAPI(externalData);
...
And then you make sure that API has all the update and event hooks:
class ExternalDataAPI {
constructor(data) {
this.data = data;
this.listeners = [];
}
addListener(fn) {
this.listeners.push(fn);
}
update(...) {
// do something with data
this.listeners.forEach(fn => fn());
}
...
}
Alternatively, there are frameworks that already do this for you (flux, etc) but they also somewhat dictate how many more things "should be done" so that might be overkill for your need.
Since your question is about organizing your code in a manageable way, I would first of all suggest pairing ReactJS with a Flux-type framework, like Redux or Relay.
If you want to skip that for now, then you can organize your project using some react components at the top of the hierarchy for storing and retrieving data. For example, in such a component, in its componentWillMount method, you can start a setTimeout that periodically checks your global array and calls setState when appropriate. The render method should then contain child components that receive this state as props.
Below is an example. Obviously, the timers can be replaced by whichever method you use to subscribe to your data changes.
// your global object
var globalState = {name: "Sherlock Holmes"}
function onData(callback) {
setInterval(function(){
callback(globalState)
}, 1500)
}
var Child = React.createClass({
render: function() {
return <h1>Hello, {this.props.name}</h1>;
}
});
var Root = React.createClass({
getInitialState: function() {
return {}
},
componentWillMount: function() {
var that = this;
this.props.onData(function(data){
that.setState({external: data})
})
},
render: function() {
if (this.state.external)
return <Child name={this.state.external.name}/>
else
return <div>loading...</div>;
}
});
ReactDOM
.render(<Root onData={onData} />, document.getElementById('container'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>