React Native: How can I focus on ListView? - reactjs

I've been working on focusing on ListView component.
Here's my code
class MyListView extends Component {
static propTypes = {
navigator: PropTypes.object.isRequired,
};
componentDidMount() {
this.listView.focus()
}
render() {
return (
<ListView
ref={ref => { this.listView = ref }}
contentContainerStyle={styles.listView}
dataSource={cloneWithRows(listDataSource)}
renderRow={(data) => <MyListViewCell
...
/>}
/>
)
}
}
export default MyListView
but it just returns an error like "this.listView.focus is not a function"
I've searched lots of articles but I couldn't find the solution.
Any ideas?

componentDidMount does not guarantee that ref prop is called before componentDidMount is called. You should change for componentDidMount code to following:
componentDidMount() {
requestAnimationFrame(()=>{
this.listView.focus()
})
}
Also if you decide to use requestAnimationFrame function. It will be better you use it using TimerMixin (see TimerMixin doc). Use react-mixin to implement Mixin in ES6 as suggested in TimerMixin doc.

Related

React KeyDown event handler in compnent - function error

I am creating a web page with ReactJs and I am stuck with one issue. I am trying to handle enter key press in my page using keydown event. Following is piece of code:
class ManageUser extends React.Component {
componentDidMount() {
window.addEventListener("keydown", this.keyEventHandler, false);
}
keyEventHandler(event: KeyboardEvent) {
if (event.keyCode === KEY_CODE.ENTER) {
this.handlerUserEntry(); //Getting error here....
}
}
handlerUserEntry() {
console.log('handle user entry here');
}
}
I am able to capture enter key event. But when I try to call this.handlerUserEntry(); from keyEventHandler function I am getting following error:
TypeError: this.handlerUserEntry is not a function
What am I doing wrong here?
As Florian suggested in their comment, any function references you pass need to be bound to your class, otherwise the this in this.handlerUserEntry will not point to the ManageUser class.
You can do this by adding a constructor with the rest of your code as-is:
class ManageUser extends React.Component {
ManageUser(props) {
this.handleUserEntry = this.handleUserEntry.bind(this);
}
...
}
I would avoid using componentDidMount() as its a deprecated feature in React's current docs.
I was able to get a working component with a little help on React Events here.
Below is my working component:
import React from 'react';
class ManageUser extends React.Component {
handler(event) {
alert(`
key: ${event.key}
keyCode: ${event.keyCode}
altKey: ${event.altKey}
ctrlKey: ${event.ctrlKey}
metaKey: ${event.metaKey}
shiftKey: ${event.shiftKey}
`)
}
render() {
return (
<div style={{display: 'flex', justifyContent: 'center'}}>
<input
placeholder='Hit a key...'
onKeyDown={this.handler}
onKeyPress={this.handler}
onKeyUp={this.handler}
/>
</div>
)
}
}
export default ManageUser
Instead of using custom keydown handler, i started using 'react-keydown' npm package and issue is resolved.

How do I call a method within a Child component from the Parent in React Native

How do I call a method within a Child component from the Parent in React Native? What I essentially want to do is emulate what componentDidMount() does for class components in a functional component.
I've been getting the error "Function components cannot be given refs" and that I may want to use React.ForwardRef().
ps. idk how i would go about reformatting the child observer, pardon the bad formatting
class Dashboard extends Component {
constructor(props) {
super(props);
this.load = React.createRef();
componentDidMount() {
this.load.current.loadAudio();
}
render(){
latestEP.query = ref => ref.orderBy("id", "desc").limit(1);
return(
{latestEP.docs.map(doc => (
<DashboardItem key={doc.id} doc={doc} ref={this.load} />
))}
)
}
}
const DashboardItem = observer(({ doc }) => {
function loadAudio(){
return console.log("works")}
return (// stuff that requires loadAudio to run first)
})
You can achieve that by using useImperativeHandle hook. Please check this out:
https://reactjs.org/docs/hooks-reference.html#useimperativehandle
Wrap DashItem in forwardRef and implement useImperativeHandle hook like below:
const DashItem = React.forwardRef(({doc}, ref) => {
useImperativeHandle(ref, () => ({
loadAudio: () => {
return console.log("works");
}
}));
...
The error "Function components cannot be given refs" should be self-explanatory: you need to change DashItem to be a class component instead of a functional component.

Material UI's withTheme() hides component from ref prop

For the rare times when you need a reference to another JSX element in React, you can use the ref prop, like this:
class Widget extends React.PureComponent {
example() {
// do something
}
render() {
...
<Widget ref={r => this.mywidget = r}/>
<OtherWidget onClick={e => this.mywidget.example()}/>
Here, the Widget instance is stored in this.mywidget for later use, and the example() function can be called on it.
In Material UI, you can wrap components around a withTheme() call to make the theme accessible in their props:
export default withTheme()(Widget);
However if this is done, the ref receives an instance of WithTheme rather than Widget. This means the example() function is no longer accessible.
Is there some way to use ref with a component wrapped by withTheme() so that the underlying object can still be accessed, in the same manner as if withTheme() had not been used?
Here is an example demonstrating the issue. Lines 27 and 28 can be commented/uncommented to see that things only fail when the withTheme() call is added.
In order to get the ref of the component which is wrapped with withStyles, you can create a wrapper around Widget, and use that with withStyles like
const WithRefWidget = ({ innerRef, ...rest }) => {
console.log(innerRef);
return <Widget ref={innerRef} {...rest} />;
};
const MyWidget = withTheme()(WithRefWidget);
class Demo extends React.PureComponent {
constructor(props) {
super(props);
this.mywidget = null;
}
render() {
return (
<React.Fragment>
<MyWidget
innerRef={r => {
console.log(r);
this.mywidget = r;
}}
/>
<Button
onClick={e => {
e.preventDefault();
console.log(this.mywidget);
}}
variant="raised"
>
Click
</Button>
</React.Fragment>
);
}
}
Have a look at this answer to see an other alternative approach
losing functions when using recompose component as ref
This is a shorter alternative based on Shubham Khatri's answer. That answer works when you can't alter the inner component, this example is a bit shorter when you can modify the inner component.
Essentially ref doesn't get passed through withTheme() so you have to use a prop with a different name, and implement ref functionality on it yourself:
class Widget extends React.PureComponent {
constructor(props) {
props.ref2(this); // duplicate 'ref' functionality for the 'ref2' prop
...
const MyWidget = withTheme()(Widget);
...
<MyWidget
ref2={r => {
console.log(r);
this.mywidget = r;
}}
/>

How to reference the leaflet layer of a react-leaflet component?

I used react-leaflet to visualize a quite long path on a map. Users can select from a list and I would like to have different color for the selected path. Changing the state and rendering again is too slow, I am looking for a faster solution.
Leaflet path elements have setStyle() method, so my first idea was using it instead of rendering again. But how to reference the leaflet layer?
class MyPathComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.selected){
this.setState({selected: true});
LEAFLET_POLYLINE.setStyle({
color: 'red'
});
}
return false;
}
render() {
return(
<Polyline polylines={this.props.path} />
);
}
}
So what should I write instead of LEAFLET_POLYLINE in this code?
Components in react-leaflet have a property called leafletElement. I believe you can do something like this:
class MyPathComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.selected){
this.setState({selected: true});
this.refs.polyline.leafletElement.setStyle({
color: 'red'
});
}
return false;
}
render() {
return(
<Polyline ref="polyline" polylines={this.props.path} />
);
}
}
Two things to note:
I haven't tested this code, so it may need some small tweaks.
Using a string for "ref" is considered legacy in React, so you'll probably want to do something slightly different (see here). The leafletElement is the important part here.
Instead of the code above, it may be better to just extend the Polyline component for your custom component (limited docs here):
import { Polyline } from 'react-leaflet';
class MyPathComponent extends Polyline {
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.selected){
this.setState({selected: true});
this.leafletElement.setStyle({
color: 'red'
});
}
return false;
}
}
Let me know if any of this works out for you.
Full example using React callback ref and adding to #Eric's answer above:
export default class MyMap extends Component {
leafletMap = null;
componentDidMount() {
console.debug(this.leafletMap);
}
setLeafletMapRef = map => (this.leafletMap = map && map.leafletElement);
render() {
return (
<Map
ref={this.setLeafletMapRef}
>
<TileLayer
attribution="Powered by <a href="https://www.esri.com">Esri</a>"
url="http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
/>
</Map>
);
}
}

"setState on undefined" this error trying to use es6 style react component in yahoo fluxible

EDIT: My mistake, my webpack hotloader was caching the old js for some reason every time I ran a build. Reset and rebuilt and it seems to be working now.
I'm trying to create a simple searchbox using es6 style class declaration in a yahoo fluxible react app. I'm working off the todo example, converting it to es6 style syntax and I'm getting an error on this.setState in the _onChange method. I've bound the functions to "this" in the constructor but I'm still getting the error.
import React from 'react';
import searchProducts from '../actions/searchProducts';
const ENTER_KEY_CODE = 13;
class SearchBox extends React.Component {
static contextTypes = {
executeAction: React.PropTypes.func.isRequired
};
static propTypes = {
text: React.PropTypes.string
};
static defaultProps = {
text:''
};
constructor(props) {
super(props);
this.state = {
text: props.text
};
this._onChange = this._onChange.bind(this);
this._onKeyDown = this._onKeyDown.bind(this);
}
render() {
return (
<input
className="search-box"
name="search-keyword"
value={this.state.text}
onChange={this._onChange}
onKeyDown={this._onKeyDown}
/>
);
}
_onChange(event, value) {
console.log( event.target.value);
//error is here///////////////////////////////////////////////////
this.setState({text: event.target.value});
}
_onKeyDown(event) {
if (event.keyCode === ENTER_KEY_CODE) {
event.preventDefault();
event.stopPropagation();
var text = this.state.text.trim();
if (text) {
this.context.executeAction(searchProducts, {
text: text
});
}
this.setState({text: ''});
}
}
}
export default SearchBox;
Edit: Disregard. I completely missed the part of the constructor where you bound the methods. Hmm.. What does the error message say exactly?
Have a look at section three of this article about refactoring react components to es6 classes.
When using the React.createClass({componentObjectLiteral}) syntax, React binds your object methods to the component instance, so that when your _onChange method gets called as your input's onChange callback function, the this keyword in your _onChange method is bound to your component.
React does not auto-bind your methods for you, so you have to do it yourself. Change your JSX to onChange={this._onChange.bind(this)}
psigns is correct React.createClass() automatically binds the methods to the component instance for you. This is not the case when you use the class syntax in React.
But there is a very neat possibility when you combine property initializers with arrow functions:
class SearchBox extends React.Component {
// …
_onChange = (event, value) => {
// …
// this will be bound to component
this.setState({text: event.target.value});
}
// …
}
Then you can use the method like you did before in the jsx part:
onChange={this._onChange} // without .bind(this)
I learned this from reading Steven Luscher's excellent post.

Resources