Testing this.props.navigation - javascript

import React from 'react';
import ListElements from '../Components/ListElements';
class HospitalsScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
Hospitals: ['Hospital de Base','Hospital Regional de Taguatinga','Hospital da Ceilandia','ClĂ­nica']
};
}
onPressItem() {
this.props.navigation.navigate('sectors');
}
render() {
return (
<ListElements
list = {this.state.Hospitals}
title='Hospitais'
onPress={this.onPressItem.bind(this)}
/>
);
}
}
export default HospitalsScreen;
I'm having some trouble testing this screen in the _onPress function.
I did not find anything that solved the problem.

In Javascript the value of this inside a function depends upon how that function is invoked.
Learn how the this binding works in JavaScript here
There are a couple of ways to achieve it. Use them and see if you get this as undefined in the function or not.
One is to add this.onPressItem = this.onPressItem.bind(this); in the constructor.
Another is arrow functions
onPressItem= (event) =>
{this.props.navigation.navigate('sectors');
}
And then there is onPress={this.onPressItem.bind(this)}.
You can find more details here
Also please go through this article Don't Use Bind When Passing Props

Related

How to pass props from an extended class in React?

Recently I've been working in a project where I need to keep the code as cleanest as possible. That's why I'm working in a component which needs to extends some characteristics from another one, but I also need to pass some props to obtain some behavior in return. But there is a problem: I'm adding a prop in the constructor, but there is a function that doesn't receive/seem have that prop:
class SampleClassOne extends PureComponent<Props>{
Constructor(Props){
console.log(props) // In this console.log, the variable aditionalVar appears
super(props){
{
}
functionOne(){
const {
varOne,
varTwo,
varThree,
aditionalVar
} = this.props
console.log(this.props) // In this console.log, the variable aditionalVar does not appear
render(){
...
}
}
}
class SampleClassTwo extends SampleClassOne {
Constructor(Props){
super({...props, aditionalVar: true}) // This is where I'm adding the new variable
}
}
}
I don't know if I'm clear with the question I'm asking... how can I make sure that new variables I'm adding from components that extends from others can be received by all the component that extends its class.
Thanks for your help

Cannot add item to JS Map

I'm simply trying to add an item to a Map (like dictionary) in Javascript. I'm using React Native.
import React, { Component } from 'react';
import {
AppRegistry,
View,
Button,
} from 'react-native';
export class Stack extends Component {
constructor(props){
super(props)
this.state = {
statelist: new Map(),
}
}
add_func_list() {
let plist = new Map()
plist.set(1, 'ddd');
alert(JSON.stringify(plist))
}
add_state_list(){
let statelist = this.state.statelist
statelist.set(2, 'ggg');
this.setState({statelist})
}
render() {
return (
<View>
<Button title="add to func list" onPress={this.add_func_list} />
<Button title="add to state list" onPress={this.add_state_list} />
</View>
);
}
}
export default function App(props) {
return (
<Stack />
);
};
AppRegistry.registerComponent('default', () => App);
If you press the first button I'm adding to a local variable in a function. Nothing gets added, the alert shows {}
The second button should add to a state variable, but I'm getting an error:
"TypeError: undefined is not an object (evaluating 'this.state.statelist')
Any ideas?
The second error is due to code error:
onPress={this.add_state_list.bind(this)}
or convert the method to a arrow function.
Regarding the alert you can't Stringify it. its data is in prototype.
Map is an iteratable.
you can do :
alert(plist.size);
or
alert(plist.get('2'));
add_func_list() {
let plist = new Map()
plist.set(1, 'ddd');
alert(JSON.stringify(plist.get(1))
}
Thanks Mukesh and Vivek. I'm new to RN so I wasn't aware of the restriction in printing a map. I actually have a bind of the method in my constructor, but failed to copy it into the example. Unfortunately the consequence was that it generated the same error as I have in my real system and I got fooled. Anyway, you answered the questions as I posted them, for which I'm thankful.
What turned out to be the real problem was a consequence of inheritance, and as I'm learning now inheritance of state doesn't work like other inheritance.
import React, { Component } from 'react';
import {
AppRegistry,
Button,
View,
} from 'react-native';
export class Stack extends Component {
constructor(props){
super(props)
this.state = {
statelist: new Map(), // get's overwritten and destroyed by child state assgnment
}
//this.statelist = new Map(), <-- this way it works fine if child sets state
this.add_state_list = this.add_state_list.bind(this)
}
add_state_list(){
this.statelist.set(2, 'ggg');
alert(JSON.stringify(this.statelist.get(2)))
}
}
export class StackHandler extends Stack {
constructor(props){
super(props)
// if I remove this.state assignment below, it works
this.state = {
count:0 // destroys parent's state
};
}
render() {
return (
<View>
<Button title="add to state list" onPress={this.add_state_list} />
</View>
);
}
}
export default function App(props) {
return (
<StackHandler/>
);
};
AppRegistry.registerComponent('default', () => App);
This code corresponds better to my problem. Class StackHandler and class Stack both have state variables. Stackhandler inherits Stack. When Stackhandler sets its state variable, it destroys the state of its parent. For us with OOP background this seems counter-intuitive. Either way, I now know not to do this and can work around it. If anyone has a good explanation, I'm all ears. In any case I'm unstuck now!

Why is "this" bound different in some instances, but the same in others in React?

In this component, I'm not able to call a function in the render method by going this.functionName if it's not an arrow function. Howevever, I am able to call this.setState effectively in both an arrow function and a regular function. Why is "this" different in some situations, but seemingly the same in other situations in a React component like this?
import React from 'react';
class Address extends React.Component {
state = {
fullAddress: "5001"
}
componentDidMount() {
this.setState({
fullAddress: "hello"
})
}
hello = () => {
this.setState({
fullAddress: "hello1"
})
}
logMessage() {
console.log(this.state.fullAddress);
}
render() {
return (
<div className="address">
{this.state.fullAddress}
<input type="button" value="Log" onClick={this.hello} />
</div>
);
}
}
export default Address;
In your example, logMessage will probably break since you need to specify your this context to it.
In this case, simply bind it in Address's constructor like so:
class Address extends Component {
constructor(props) {
super(props)
this.logMessage = this.logMessage.bind(this)
}
}
A second approach would be the same you already used with hello as arrow function like. Arrow functions keep your current context (this) and that's why you have access to this.setState inside hello's body for example.

How to avoid using inline functions in React (ES6) for functions that take arguments

I'm trying to follow the suggestion in this react-eslint doc to avoid using inline functions.
I have a div with an onClick funciton like so:
onClick={ () => this.props.handleClick(some_constant) }
This works perfectly fine, however I don't want to have an inline function. When I try to abstract it by following the pattern in the provided link above, I get a setState error that runs infinitely.
class Foo extends React.Component {
constructor() {
super();
this._handleClickWrapper = this.handleClickWrapper.bind(this);
}
_handleClickWrapper() {
// handleClick is a callback passed down from the parent component
this.props.handleClick(some_constant)
}
render() {
return (
<div onClick={this._handleClickWrapper}>
Hello!
</div>
);
}
}
What needs to be done so that I can avoid using inline functions?
Edit:
I made a serious typo, but in my code, I have what is currently reflected and it is still causing the error.
You bound the wrong function to this. It should be:
this._handleClickWrapper = this._handleClickWrapper.bind(this);
This way _handleClickWrapper will always be bound to the context of the component.
If you really really really want to follow the jsx-no-bind rule, you can create a new component and pass someConstant in as a prop. Then the component can call your callback with the value of someConstant:
class FooDiv extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
render() {
return <div onClick={this.handleClick}>Hello!</div>
}
handleClick() {
this.props.onClick(this.props.someConstant);
}
}
Then your Foo component can just do this:
class Foo extends React.Component {
render() {
const someConstant = ...;
return (
<FooDiv
onClick={this.props.handleClick}
someConstant={someConstant}
/>
);
}
}
Having said that, I would recommend not following jsx-no-bind and just use bind or arrow functions in render. If you're worried about performance due to re-renderings caused by using inline functions, check out the reflective-bind library.
There is a typo
this._handleClickWrapper = this.handleClickWrapper.bind(this);
should be
this._handleClickWrapper = this._handleClickWrapper.bind(this);
in your constructor you forgot to pass props to super()
constructor(props) {
super(props);
this._handleClickWrapper = this._handleClickWrapper.bind(this);
}
Tipp: You can avoid binding (and even the constructor) by using arrow functions declaration inside the class (babel-preset-es2016).
class Foo extends React.Component {
state = {} // if you need it..
onClick = () => {
this.props.handleClick(some_constant)
}
render() {
return (
<div onClick={this.onClick}>
Hello!
</div>
);
}
}
This way you components gets smaller, and easier to read.
https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding

React ES6 component inheritance: working, but not recommended?

I'm currently inheriting an ES6 React base component in the following way:
model.js (base component):
class ModelComponent extends React.Component {
render() {
// Re-used rendering function (in our case, using react-three's ReactTHREE.Mesh)
...
}
}
ModelComponent.propTypes = {
// Re-used propTypes
...
};
export default ModelComponent;
Then I have two extending components that both look basically like this:
import ModelComponent from './model';
class RobotRetroComponent extends ModelComponent {
constructor(props) {
super(props);
this.displayName = 'Retro Robot';
// Load model here and set geometry & material
...
}
}
export default RobotRetroComponent;
(Full source code here)
This appears to work fine. Both models are showing up and working as I would expect.
However, I have read in multiple places that inheritance is not the correct approach with React - instead I should be using composition. But then again, Mixins are not supported in React v0.13?
So, is the approach I'm taking above OK? If not, what's the problem, and how should I do this instead?
The Facebook team recommends 'using idiomatic JavaScript concepts' when writing React code, and since there is no mixin support for ES6 classes, one should just use composition (since you are just making use of idiomatic Javascript functions).
In this case, you can have a composeModal function that takes a component and returns it wrapped in a higher-order, container component. This higher-order component will contain whatever logic, state, and props you want passed down to all of its children.
export default function composeModal(Component){
class Modal extends React.Component {
constructor(props){
super(props)
this.state = {/* inital state */}
}
render() {
// here you can pass down whatever you want 'inherited' by the child
return <Component {...this.props} {..this.state}/>
}
}
Modal.propTypes = {
// Re-used propTypes
...
};
return Modal
}
Then you can use the composition function like so:
import composeModal from './composeModal';
class RobotRetroComponent extends React.Component {
constructor(props) {
super(props);
this.displayName = 'Retro Robot';
// Load model here and set geometry & material
...
}
render(){
return /* Your JSX here */
}
}
export default composeModal(RobotRetroComponent);

Categories