I have a class Two with a bunch of functions inside. Some of them are using this.setState({}) and they throw me a warning: setState(...): Can only update a mounted or mounting component.
Here's an example of the code:
class One extends React.Component {
constructor() {
super()
this.two = new Two;
}
componentDidMount() {
this.two.hello()
}
render() {
return (<View><Text>Hello World!</Text></View>)
}
}
class Two extends React.Component {
constructor() {
super()
this.state = {
connected: false
}
}
hello() {
this.setState({connected: true}) //This one throw the warning
}
}
Is there any way to do things in a better way? Since my class Two is functionnal, I would like to not change the code too much to have things working. Btw, I need to the this.two = new Two line.
Should I create a library, a module, or whatever? If so, can you give me a good tutorial?
If you are not mounting the Component, React can’t update it’s state using it’s built-in state handler.
And since you are not mounting it, it should probably not be a react component at all. Try using a regular class instead:
class Two {
constructor() {
super()
this.state = {
connected: false
}
}
hello() {
this.state.connected = true
}
}
The main problem here is you are trying to make components in React communicate in an unconventional way.
The way you want to go about this is to make use of props. This is how components talk to each other in React, instead of being direct, like you're attempting.
Check out my code here where I'm doing the same as you
Basically, I've written 2 examples in 1 here. The first is passing just raw data through to another component. (this.props.data.someData). And the second, which is more like what you're wanting to do, is using React's Life Cycle methods, to listen for when a function should run, through prop activation.
What this means in my example, is when the runFunction prop is passed into Two, either when it's first created componentDidMount() or when set to true later componentWillRecieveProps(), it will run the testFunction()
Related
I would like to pass my object based on JavaScript class from mixin to component without reactivity. I use TypeScript, so I can't set the object to this of mixin without setting types in data(). But data() reactivity breaks some thing in my object.
mixin.js:
export default {
setup() {
const foo = new Foo()
return {
foo,
}
}
}
component.js:
import mixin from './mixin.js'
export default {
setup() {
// How can I get foo here?
}
}
Update 1
Yes, it is good solution for using only one foo instance for everywhere.
But how can I use different foo instances for each component?
Same as in JS classes:
class Mixin {
foo() {
// ...
}
}
class Component extends Mixin {
bar() {
this.foo()
}
}
mixin.js
export const foo = new Foo()
Import (and use) foo anywhere in your app, including any setup() function:
import { foo } from './path/to/mixin'
The above uses the same instance everywhere. If you want to use different instances of foo in each separate component, mixin.js:
export const useFoo = () => new Foo()
Anywhere else:
import { useFoo } from './path/to/mixin'
const foo = useFoo()
However, take note the second approach creates a new intance of Foo() every time useFoo() is called. So once you called it, you must use foo in that component.
Calling useFoo() multiple times in the same component will generate multiple instances, unlike how you'd use useStore(), for example.
But, I'm wondering, why do you need a mixin in the first place? Why not use:
const foo = new Foo()
...in the components where you need it?
What are you trying to achieve? And, more importantly, why?
I created a class in react native.
import Author from "./authorDetails";
class MainFeedPost {
id;
description;
postImage;
author;
creationDateTime;
version;
status;
extra = {
likes,
shares,
comments
};
constructor() {
this.author = new Author();
}
}
export default MainFeedPost;
Now I want to set this as a type in one of my components state. I tried it like this an it is undefined.
import { MainFeedPost } from "../../models";
class SharePostScreen extends Component {
constructor(props) {
super(props);
this.state = {
post: MainFeedPost
};
}
doesn't this support in react native? I want to initialize the shape of my object with MainFeedPost class.
You told "as a type". If you want static type checking, you need something like flow.
If you wanna value in the state, you must place there an instance:
...
this.state = {
post: new MainFeedPost(),
};
...
UPD:
I got your point. Because of default export you need to use import MainFeedPost from "...", without braces.
I'm building two widgets with mobx/react, where all the logic sits inside the stores. Both share most of the design rules, so their stores are 95% identical.
Is there a smart way to handle this situation?
For example, is it possible to create inheritance such as this?
class Animal {
#observable name = "";
constructor(name) {
this.name = name;
}
#computed get sentence() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
#observable isBarking = false;
#computed get bark() {
if (this.isBarking){
console.log('The dog is barking');
}
}
#action
setIsBarking(isBarking) {
this.isBarking = isBarking;
}
}
Yes you can, but you have to structure it like this, using the new Mobx pattern which does not use decorators:
(Using Typescript)
import {observable, action, computed, makeObservable} from "mobx";
const animalProps = {
name: observable,
sentence: computed
};
class abstract Animal {
name = "";
constructor(name) {
this.name = name;
}
get sentence() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
isBarking = false;
constructor(){
makeObservable(this, {
...animalProps,
isBarking: observable,
bark: computed,
setIsBarking: action
});
}
get bark() {
if (this.isBarking){
console.log('The dog is barking');
}
}
setIsBarking(isBarking) {
this.isBarking = isBarking;
}
}
If you need an instance of Animal in your app, then Mobx-State-Tree is a better option. Because making a prop observable/actionable/computable twice (the parent class and the subclass) will throw an error.
I know this was asked a long time ago at this point, but per the docs here you can override as you wrote. There are limitations though:
Only action, computed, flow, action.bound defined on prototype can be overriden by subclass.
Field can't be re-annotated in subclass, except with override.
makeAutoObservable does not support subclassing.
Extending builtins (ObservableMap, ObservableArray, etc) is not supported.
You can't provide different options to makeObservable in subclass.
You can't mix annotations/decorators in single inheritance chain.
All their standard limitations apply as well which I won't list here.
This works with the non-annotation syntax as well (e.g., makeObservable).
Have you consider MobX State Tree (https://github.com/mobxjs/mobx-state-tree) for managing your two classes Animal and Dog ?
This will give you the powerfull compose functionality, that could be used instead of inheritance.
Here's the probably most useful part for you: "Simulate inheritance by using type composition" https://github.com/mobxjs/mobx-state-tree#simulate-inheritance-by-using-type-composition
Can anyone point a direction of the Component syntax using the latest React-Native init cmd? I don't recall ES6 class syntax quite like it.
export default class App extends Component<{}> {
}
Where, I've seen the following without brackets.
export default class App extends Component {
}
Thanks
Reference : https://reactjs.org/docs/components-and-props.html
class Example extends Component {
render() {
return <h1>Hello</h1>;
}
}
This is the ES6 way of defining components in react.
I am looking for specialized singleton implementation, probably I might be using wrong terminology and hence looking for expert suggestion. Here is my scenario:
There is common code which can be called by ComponentA or ComponentB. I need to push telemetry data from the common code. Telemetry needs to have information that whether this common code get called by ComponentA or ComponentB.
So common code will have just this line of code:
telemetry.pushData(this._area, data);
where this._area tells the telemetry data is getting pushed for which component
I need to push telemetry data from multiple places so it would be good if object got created once and used through out the code lifetime
One option I can think of passing component context to the common code which in mind doesn't look right, hence looking for suggestion what kind of pattern one should use in this case?
This is what I am thinking
// Telemetry.ts file present in shared code
export class Telemetry extends Singleton {
public constructor() {
super();
}
public static instance(): Telemetry {
return super.instance<Telemetry>(Telemetry);
}
public publishEvent(data): void {
if (!this.area) {
throw new Error("Error: Initialize telemetry class with right area");
}
pushtelemetryData(this.area, data);
}
public area: string;
}
// Create Telemetry object from component A
Telemetry.instance().area = "ComponentA";
// Shared code will call telemetry publishEvent
Telemetry.instance().publishEvent(data);
Thanks
It's not a good pattern to use in TypeScript where you would generally inject dependencies.
If you must absolutely do it then you can do it by faking it somewhat:
namespace Telemetry {
var instance : SingletonSomething;
export function push(data: Any) : void {
if (instance == null) {
instance = new SingletonSomething();
}
instance.push(data);
}
class SingletonSomething() { ... }
}
and then you could call
Telemetry.push(data);
You can imitate the singleton pattern in typescript easily:
class Telemetry {
private static instance: Telemetry;
public static getInstance(): Telemetry {
if (Telemetry.instance == null) {
Telemetry.instance = new Telemetry();
}
return Telemetry.instance;
}
...
}
If you have your code in some sort of closure (module, namespace, etc) then you can replace the static member with:
let telemetryInstance: Telemetry;
export class Telemetry {
public static getInstance(): Telemetry {
if (telemetryInstance == null) {
telemetryInstance = new Telemetry();
}
return telemetryInstance;
}
...
}
But then you can also replace the static method with:
let telemetryInstance: Telemetry;
export function getTelemetryInstance(): Telemetry {
if (telemetryInstance == null) {
telemetryInstance = new Telemetry();
}
return telemetryInstance;
}
export class Telemetry {
...
}
At this point, in case you are using some sort of closure, you might ask yourself if you really need the class at all?
If you use this as a module:
// telemetry.ts
export interface TelemetryData {
...
}
export function pushData(data: TelemetryData): void {
...
}
Then you get exactly what you're looking for, and this is more of the "javascript way" of doing it.
Edit
In the telemetry module there's no need to know the users of it.
If the Telemetry.pushData function needs to have information about the object that called it then define an interface for it:
// telemetry.ts
export interface TelemetryData {
...
}
export interface TelemetryComponent {
name: string;
...
}
export function pushData(data: TelemetryData, component: TelemetryComponent): void {
...
}
Then in the other modules, where you use it:
// someModule.ts
import * as Telemetry from "./telemetry";
class MyComponent implement Telemetry.TelemetryComponent {
// can also be a simple string property
public get name() {
return "MyComponent";
}
fn() {
...
Telemetry.pushData({ ... }, this);
}
}
2nd Edit
Because you are using a module system, your module files are enough to make singletons, there's no need for a class to achieve that.
You can do this:
// telemetry.ts
let area: string;
export interface TelemetryData {
...
}
export function setArea(usedArea: string) {
area = usedArea;
}
export function pushData(data: TelemetryData): void {
...
}
Then:
Telemetry.setArea("ComponentA");
...
Telemetry.publishEvent(data);
The telemetry module will be created only once per page, so you can treat the entire module as a singleton.
Export only the functions that are needed.