react native: attempted to assign to readonly property - reactjs

I create a React ref by calling React.createRef in React Native. Then I assign it to a ref. I got the error: Attempted to assign to readonly property
export default class List extends PureComponent<Props, object> {
private flatListRef: React.RefObject<FlatList<any>>;
constructor(props) {
super(props);
this.flatListRef = React.createRef();
}
render() {
return (
/.../
<FlatList ref={this.flatListRef}></FlatList>
)
}
}
But When I use the callback way to assign the react ref, everything is ok.
<FlatList ref={ele => { this.flatListRef = ele }}></FlatList>
I have no idea what's the difference between the two ways

the ref property in React is expecting a function and it's called immediately after the component is mounted. You can do other things besides setting a reference.
https://zhenyong.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute

Related

ReactTS component class: how to save ref on the element

I use TypeScript and class component way and I need to save reference on my element. I am going to get the state of target element when 'resize' event will happen.
Accordingly How to use refs in React with Typescript I tried this:
export default class Carousel extends Component<CarouselProps, CarouselState> {
private ref: React.LegacyRef<HTMLDivElement>;
constructor(...) {
this.ref = React.createRef();
window.addEventListener('resize', (e) => this.resizeCarouselElements());
}
componentDidUpdate(...){...}
componentDidMount(...){...}
resizeCarouselElements = () => {
<...get current state of target element here ...>
}
render() {
return (
<div className='name' ref={this.ref}>
})
}
But after resizing the window I have got null:
How can I save reference on my div className='name' and use it after user's window resizing? Which way is the best practice? Store the ref in state, const or somewhere else?

Get HTMLInputElement from ref to PrimeReact's InputText

I am trying to use a ref to get to the underlying input element of an InputText component. I used this.textFieldRef = React.createRef() to set up the ref, and then the attribute ref={this.textFieldRef} to hook it up. Yet in the componentDidMount I cannot use this.textFieldRef.current.select() because select() is not a function available for that object. So somehow, InputText is not returning the underlying HTMLInputElement.
Does anyone know how I can get from a ref to something that allows me to select() the text in the InputText element?
Here is my code, which is actually in TypeScript...
import * as React from 'react';
import { InputText } from 'primereact/inputtext';
export class ValueCard extends React.Component<{}, {}> {
textFieldRef: React.RefObject<any> = React.createRef();
componentDidMount = () => {
if (this.textFieldRef.current instanceof InputText) {
this.textFieldRef.current.select();
}
}
render() {
return = (
<InputText
value="test"
ref={this.textFieldRef}
/>
);
}
}
Looking at the source for PrimeReact's InputText component (source), they are attaching a reference to the inner input element to this.element.
This allows you to just add .element to your reference:
this.textFieldRef.current.element.select();
I tested it out in this sandbox, and it seems to work as expect:
https://codesandbox.io/s/203k7vx26j
Maybe you could try to use react-dom library:
ReactDOM.findDOMNode(this.textFieldRef.current).querySelector('input');

react mobx, component does not render new observable

I am using Mobx with React (typescript) to get some data from firebase and display it. at the first render no data is rendered on the screen till I change for instance tabs in view that the component get re-rendered.
the component class:
interface Props {id: string; mytore?: MyStore;}
#inject('mystore')
#observer
export class myClass extends Component<Props>{
constructor(props: Props) {super(props);}
componentDidMount() { this.props.mystore!.getBs(this.props.id);}
render() {
const { arrB } = this.props.mystore!;
return (
<div>{arrB.map((e) => (<h3 key={`${e.id}`}>{e.name}</h3>))}</div>
);
}
}
the store class
export class MyStore{
#observable arrA=[];
#observable arrB=[];
constructor(){this.loadAllAs()}
#action.bound loadAllAs(){runInAction(async()=>(this.arrA=await /*fetchfunction*/))}
#action getBs(id){this.arrB=arrA.filter(/*some logic*/)}
}
so here the arrB gets manipulated after calling the method, and it is an observable which get destructures at render method of the component so I was expecting any changes to the arrB result in a re-render but nothing happens till regarding other actions componentDidMount gets called.
Because arrB must be #computed
computed
#computed
getBs(id){
return this.arrA.filter(/*some logic*/)}
}
const { getBs } = this.props.mystore!;
getBs(id).map...

componentDidMount() not being called when react component is mounted

I've been attempting to fetch some data from a server and for some odd reason componentDidMount() is not firing as it should be. I added a console.log() statement inside of componentDidMount() to check if it was firing. I know the request to the server works as it should As I used it outside of react and it worked as it should.
class App extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
obj: {}
};
};
getAllStarShips () {
reachGraphQL('http://localhost:4000/', `{
allStarships(first: 7) {
edges {
node {
id
name
model
costInCredits
pilotConnection {
edges {
node {
...pilotFragment
}
}
}
}
}
}
}
fragment pilotFragment on Person {
name
homeworld { name }
}`, {}). then((data) => {
console.log('getALL:', JSON.stringify(data, null, 2))
this.setState({
obj: data
});
});
}
componentDidMount() {
console.log('Check to see if firing')
this.getAllStarShips();
}
render() {
console.log('state:',JSON.stringify(this.state.obj, null, 2));
return (
<div>
<h1>React-Reach!</h1>
<p>{this.state.obj.allStarships.edges[1].node.name}</p>
</div>
);
}
}
render(
<App></App>,
document.getElementById('app')
);
The issue here is that the render method is crashing, because the following line is generating an error
<p>{this.state.obj.allStarships.edges[1].node.name}</p>
Fix this to not use this.state.obj.allStarships.edges[1].node.name directly, unless you can guarantee that each receiver is defined.
Check your component's key
Another thing that will cause this to happen is if your component does not have a key. In React, the key property is used to determine whether a change is just new properties for a component or if the change is a new component.
React will only unmount the old component and mount a new one if the key changed. If you're seeing cases where componentDidMount() is not being called, make sure your component has a unique key.
With the key set, React will interpret them as different components and handle unmounting and mounting.
Example Without a Key:
<SomeComponent prop1={foo} />
Example with a Key
const key = foo.getUniqueId()
<SomeComponent key={key} prop1={foo} />
Also check that you don't have more than one componentDidMount if you have a component with a lot of code. It's a good idea to keep lifecycle methods near the top after the constructor.
I encountered this issue (componentDidMount() not being called) because my component was adding an attribute to the component state in the constructor, but not in the Component declaration. It caused a runtime failure.
Problem:
class Abc extends React.Component<props, {}> {
this.state = { newAttr: false }; ...
Fix:
class Abc extends React.Component<props, {newAttr: boolean}> {
this.state = { newAttr: false }; ...

"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