Reactjs - accessing variables - variables

How can I access variable bvar in the code below? Also, when would I declare variables as:
a) state
b) between constructor() and render()
c) inside render() - my understanding is that I'd set them here if a variable can change and I'd like to set it each time a component renders. So if I know something is not changing at all, it'd be a const and where would I set it?
import React, {Component} from 'react';
export default class App extends Component {
constructor(props) {
super();
// Set the initial grid in
this.state = {
value: 4,
xsquares: 10,
ysquares: 10
};
var bvar = "cat";
}
render() {
var avar = [
"Hydrogen",
"Helium",
"Lithium",
"Beryl­lium"
];
let cvar = "dog";
return (
// Add your component markup and other subcomponent references here.
<div>
<h1>Hello, World! {this.state.value}</h1>
<h2>{this.state.xsquares}</h2>
<h3>{avar[0]}</h3>
<h4>{this.bvar}</h4>
<h3>{cvar}</h3>
</div>
);
}
}
All variables display apart from bvar.

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"

import React, {Component} from 'react';
export default class App extends Component {
constructor(props) {
super();
// Set the initial grid in
this.state = {
value: 4,
xsquares: 10,
ysquares: 10
};
this.bvar = "cat";
}
render() {
var avar = [
"Hydrogen",
"Helium",
"Lithium",
"Beryl­lium"
];
let cvar = "dog";
return (
// Add your component markup and other subcomponent references here.
<div>
<h1>Hello, World! {this.state.value}</h1>
<h2>{this.state.xsquares}</h2>
<h3>{avar[0]}</h3>
<h4>{this.bvar}</h4>
<h3>{cvar}</h3>
</div>
);
}
}

Related

React Native viro react VR SkyBox is not working

I am developing a VR application using ViroReact, https://viromedia.com/viroreact. But I am having problem with using SkyBox (Cubemap) VR scene. Everything is working fine when I used Viro360Image view.
This is my VR scene using Viro360Image view
export default class HotelRoomVRScene extends Component {
constructor() {
super();
this.state = {} // Set initial state here
}
render() {
return (
<ViroScene>
<Viro360Image source={require('./res/hotel-room.jpg')} />
</ViroScene>
)
}
}
module.exports = HotelRoomVRScene;
The above scene is working fine. I can view the VR experience in the VR headset as well. But when I changed it to the Skybox version as below. It stopped working.
export default class HotelRoomVRScene extends Component {
constructor() {
super();
this.state = {} // Set initial state here
}
render() {
return (
<ViroScene>
<ViroSkybox source={{nx: require('./res/px.jpg'),
px: require('./res/px.jpg'),
ny: require('./res/px.jpg'),
py: require('./res/px.jpg'),
nz: require('./res/px.jpg'),
pz: require('./res/px.jpg')}} />
</ViroScene>
)
}
}
module.exports = HotelRoomVRScene;
The above code is throwing the following error.
So why is the Skybox version not working?
On your render method:
render() {
return (
<ViroScene>
<ViroSkybox source={{nx: require('./res/px.jpg'),
px: require('./res/px.jpg'),
ny: require('./res/px.jpg'),
py: require('./res/px.jpg'),
nz: require('./res/px.jpg'),
pz: require('./res/px.jpg')}} />
</ViroScene>
)
}
you have a typo for the skybox, its instead of
referring to: https://docs.viromedia.com/docs/viroskybox1
and also you are exporting the same class two times, one in:
export default class HotelRoomVRScene extends Component {
the other one in :
module.exports = HotelRoomVRScene;
I suggest you remove the latter one.

Handling input changes in mobx

Let's say that I have two forms, each related to a seperate mobx store. One form is for Client info (first name, last name etc), and the other for Employee info. Each form obviously has multiple inputs that update the observables in the related store.
In this example I have an action in each store that takes an event and based on the name, updates the value:
#action handleInputChange = (e) => {
this[e.target.name] = e.target.value
}
Is there a way to abstract this action into a helper file, something that would contain common actions, instead of retyping this again and again?
Thanks in advance, I'm pretty new to this as you can imagine.
There are several ways to handle the question. In my project, I just wrote an HOC(Higher-Order Component) to do that.
export default function asForm(MyComponent, formDataProp) {
return #observer class Form extends Component {
// constructor, etc.
updateProperty(key, value) {
this.props[formDataProp][key] = value;
}
// some other functions like double click prevention, etc.
render() {
return (
<MyComponent
{...this.props}
updateProperty={this.updateProperty}
// some other props
/>
);
}
};
}
Then use the HOC like this:
#observer
class UserForm extends Component {
render() {
const { updateProperty, userInfo } = this.props;
return (
<div className="form-wrapper">
<YourInputComponent
name="name"
updateProperty={updateProperty}
value={userInfo.name}
// other props
/>
</div>
);
}
}
UserForm.propTypes = {
userInfo: PropTypes.instanceOf(UserInfo),
updateProperty: PropTypes.func.isRequired,
};
export default asForm(UserForm, 'userInfo');
I am not sure if this solution violates the rule that you should not assign values to props.

Compute mobx value from action

I'm currently refactoring a FreeCodeCamp repo as way to learn mobx. What I'm trying to do is calculate this.store.studentCount as you can see in the StudentModal Component. Take a look here:
This is my /client/src/components/students/Modal.js
#observer
#inject('StudentModal')
export default class StudentModal extends Component {
store = new this.props.StudentModal()
renderStudentCount() {
let message
if (this.store.studentCount > 1) {
message = `${this.store.studentCount} students`
} else {
message = `${this.store.studentCount} student`
}
return <div id="student-count">{message}</div>
}
render() {
return (
<div className="AddStudentForm">
<div className="class-table-button-container">
<Button
onClick={this.open}
>
Add Student
</Button>
{this.renderStudentCount()}
</div>
.....
)
}
}
Taking a look at my models for the Modal component you can see I need to fetch a service to get the length of this but for whatever reason I cannot set the studentCount to a new value.
This is my /client/src/models/students/Modal.js
import { observable, action } from 'mobx'
import StudentsService from '../../services/StudentsService'
export default class StudentModal {
#observable open = true
#observable studentCount = 0
#action
fetchStudents() {
StudentsService.fetchStudents().then(response => {
const studentCount = response.body
this.studentCount = studentCount.length
})
}
}
You can take a look at the full source code here: https://github.com/imcodingideas/classroom-mode/tree/mobx-migration which I should remind you that this is open-source.
Am I doing this correctly? Do you have any feedback for me?
There seem to be some minor things:
Identical class names
This might lead to problems since, both your store and react component are called StudentModal
decorator order
as #Joseph suggested swap the order around your class:
#inject("StudentModal")
#observer
export default class StudentModal
State management
store = new this.props.StudentModal()
On creation of every StudentModal you seem to create a new state store. Normally te store is instantiated once (unless you really want seperate stores per modal) inside your entry point and then used later on:
import { render } from "react-dom";
import { Provider } from "mobx-react";
var stores = { StudentModal: new StudanModalStore() }
render(
<Provider {...stores}>
<StudentModal />
</Provider>,
rootEl,
);
#observer
#inject('StudentModal')
export default class StudentModal extends Component {
//used getter instead of setting once
// no longer use `new` but directly reference instance of the store.
get store (): StudentModalStore { return this.props.StudentModalas; }
}
above code is in typescript.

mobx, render when a property value changes

I'm just getting started with Mobx in a react-native project and am having trouble understanding how to perform changes on a observed object.
Changing the object reference via the setWorkingObject action function in my store properly renders the UI, however if I just want to change a single property within this object, how do I cause a render?
My "store":
export default class MyStore {
constructor() {
extendObservable(this, {
workingObject: null
});
}
}
My "container":
class Container extends Component {
render() {
return (
<Provider store={new MyStore()}>
<App />
</Provider>
);
}
}
and my "component", which uses a simple custom input component (think of it like Checkbox) to perform changes to a property of my workingObject
class MyClass extends Component {
...
render() {
const {store} = this.props;
return
<View>
...
<RadioGroup
options={[
{ title: "One", value: 1 },
{ title: "Two", value: 2 }
]}
onPress={option => {
store.workingObject.numberProperty = option.value;
}}
selectedValue={store.workingObject.numberProperty}
/>
...
</View>
}
}
export default inject("store")(observer(MyClass));
I can't figure out why this doesn't work, in fact it looks very similar to the approach used in this example
Any other tips/critique on how I've implemented mobx welcome
The problem is that only existing properties are made observable at the time the workingObject is first assigned.
The solution is to declare future properties at the time of assignment, ie:
// some time prior to render
store.workingObject = { numberProperty:undefined };
First, you don't want to set initial value to null. Second, adding properties to observable object after it was created will not make added properties observable. You need to use extendObservable() instead of assigning new properties directly to observable object. Another solution is to use observable map instead.
in your store:
extendObservable(this, {
workingObject: {}
});
in your component:
extendObservable(store.workingObject, {numberProperty: option.value});
I recommend using Map in this case:
extendObservable(this, {workingObject: new Map()});
in your component:
store.workingObject.set(numberProperty, option.value);

Which way better to pass object to a Dump Component in React Native?

Consider I have a dump component, use for display user's information.
There're 2 ways to pass user information to component:
1):
class UserItem extends PureComponent {
// pass user's properties
const {
userName, userEmail,
} = this.props;
// same
render() {
return (
<View>
<Text>{userName}</Text>
<Text>{userEmail}</Text>
</View>
);
}
}
2):
class UserItem extends PureComponent {
// pass a user object
const {
userName, userEmail,
} = this.props.user;
// same
render() {
return (
<View>
<Text>{userName}</Text>
<Text>{userEmail}</Text>
</View>
);
}
}
So which way I should use? I will list down some pros & cons that I know in 2 ways:
1)
pros:
the logic is very easy to understand, there're no "magic" things happen, the component display whatever you pass in.
can use the power of PureComponent by shallow compare state & props (https://reactjs.org/docs/shallow-compare.html) so that the component only get re-render whend needed
cons:
I have to typing many parameters passing, like <UserItem userName={user.name} userEmail={user.email}> (unless you use spreading operator ...user, but you will pass all the object properties)
I cannot use object method inside the component. for example, my User model has a method user.totalMoneys(), because there're some lazy calculated properties in my object.
2)
pros:
passing looks simple: <UserItem user={user}>
can use object method inside UserItem
cons:
I cannot use the benefit of PureComponent, I have to compare my own,
I thing the best to go is the 1, i often see the spread operator used for this, btw you can do this in the parent to avoid passing useless props :
class UserParent extends Component {
const { uselessProp1, uselessProp2, ...usefullProps } = this.props;
render() {
return (
<View>
<UserItem {...usefullProps} />
</View>
);
}
}