How use Mobx 6 storage with React 17? - reactjs

I'm completely confused with the rules of mobX - react . Some methods in another project work, but not in this test.
Below is the code of my components from the test application
App.js
import React, { FC } from 'react';
import "./App.css";
import {observer} from 'mobx-react';
import ComponentFirst from './components/ComponentFirst/ComponentFirst';
import ComponentSecond from './components/ComponentSecond/ComponentSecond';
const App: FC = observer(() => {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<ComponentFirst />
<ComponentSecond />
</div>
);
})
export default App;
ComponentFirst
import React, { FC } from 'react';
import {testStoreFirst} from '../../stores/testStoreFirst';
const ComponentFirst : FC = () => {
return (
<div>
<h3>It is First Component</h3>
<p>{testStoreFirst.testText}</p>
<p>{testStoreFirst.textForSecondTestStore}</p>
<button onClick={() => {testStoreFirst.setTestText('New text after click')}}>Click me!!!</button>
</div>
)
}
export default ComponentFirst;
ComponentSecond
import React, { FC } from 'react';
import {testStoreSecond} from '../../stores/testStoreSecond';
import {testStoreFirst} from '../../stores/testStoreFirst';
const ComponentSecond : FC = () => {
return (
<div>
<h3>It is Second Component</h3>
<p>{testStoreSecond.textFromFirstStore}</p>
<button onClick={() =>{testStoreFirst.setTextForSecondTestStore('I can change text from second storage')}}>Click me!!!</button>
</div>
)
}
export default ComponentSecond;
testStoreFirst
import { makeAutoObservable} from "mobx";
class TestStoreFirst {
testText='It is test text from mobX storage';
textForSecondTestStore='this text from First Store!!!';
constructor() {
makeAutoObservable(this);
}
setTextForSecondTestStore = (newText : string) => {
this.textForSecondTestStore = newText;
}
setTestText = (newText: string) => {
this.testText = newText;
console.log('It is not work');
}
}
export const testStoreFirst = new TestStoreFirst()
testStoreSecond
import {makeAutoObservable} from 'mobx'
import {testStoreFirst} from './testStoreFirst'
class TestStoreSecond {
textFromFirstStore = testStoreFirst.textForSecondTestStore
constructor() {
makeAutoObservable(this);
}
}
export const testStoreSecond = new TestStoreSecond();
My question
My App component is subscribed via observe to changes in stores. By clicking on the first button, in 1 component the text in the storage and, accordingly, the text should change, but it does not change. And In the second component, the value for the text field is taken from testStoreSecond. There, the text field is taken from testStoreFirst . When the button is clicked, the method from testStoreFirst is executed which should change the text, but it does not change.
I've read the documentation, but I still don't fully understand how to use the store and achieve "reactivity" and instant change of the component.

You need to wrap every component that uses any observable values with observer decorator, like you did with an App. But in case of App it's actually useless because you are not using observable values there. So just wrap other components and it should work fine.
As for this line textFromFirstStore = testStoreFirst.textForSecondTestStore it won't work like you expected because you just assigned value of testStoreFirst.textForSecondTestStore to textFromFirstStore and that's it.
To make such value reactive you need to use computed value. To make computed you just need to setup a getter function, like that:
class TestStoreSecond {
// ...
get textFromFirstStore() {
return testStoreFirst.textForSecondTestStore
}
// ...
}
// And in React access it just as before (it's a getter, not a function)
<p>{testStoreSecond.textFromFirstStore}</p>

Related

Dynamically importing of components - Got error "React.createElement: type is invalid"

I'm having trouble with dynamically importing components in my code. The error message is:
Warning: React.createElement: type is invalid -- expected a string
(for built-in components) or a class/function (for composite
components) but got: object. You likely forgot to export your
component from the file it's defined in, or you might have mixed up
default and named imports.
I know that there are a lot of the same errors to find in the web and I already read, that it could be an issue of how I importing the namespace, but actually I did not get it to work.
Maybe someone could give a hint or a solution to my problem:
The situation:
Depending on the navigation property I want to import the suitable component view. So the value of this.props.navigation is a string value, which possible value is i.e. SignInView. When you open the page first time the value is null.
This is the code of my App.js file:
import React, { Component, lazy, useEffect, useState } from 'react';
import NavBarComponent from './components/navbar.component';
import { connect } from 'react-redux';
import { updateNavigation, updateUser, updateLanguage } from './actions/index';
const importView = async view => {
if(view === undefined || view === null) {
view = "Null";
}
return lazy(() =>
import(`./views/${String(view).toLowerCase()}View`)
.catch(() =>
import(`./views/nullView`)
))
};
class App extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
const View = importView(this.props.navigation);
return (
<div>
<NavBarComponent
key={"navBarComponent"}
/>
{this.props.navigation}
<React.Suspense fallback='Loading view...'>
<View key={"view"} />
</React.Suspense>
</div>
);
}
}
function mapStateToProps({ user, navigation, language}) {
return {
user,
navigation,
language,
};
}
function mapDispatchToProps(dispatch) {
return {
updateUser: user => dispatch(updateUser(user)),
updateNavigation: navigation => dispatch(updateNavigation(navigation)),
updateLanguage: language => dispatch(updateLanguage(language)),
};
}
const Form = connect(
mapStateToProps,
mapDispatchToProps
)(App);
export default Form;
As you can see in the render() method, I want to import the view with this line:
const View = importView(this.props.navigation);
Here are two examples of components (views), which I want to import:
signinView.js:
import React from 'react';
export const SignInView = () => {
return (
<div>Sign In View </div>
);
}
export default SignInView;
nullView.js:
import React from 'react';
const NullView = () => {
return (
<div>No View found</div>
);
}
export default NullView;
Could it be an issue of upper / lowercase letters?
Here is one of many alternatives solutions, if you notice, the components in VIEWS object are not initialized, and they will only render on JSX invoke:
import NullView from '...';
import StringView from '...';
const VIEWS = {
stringView: StringView,
nullView: NullView,
};
class App extends Component {
render() {
const View = VIEWS[this.props.navigation];
return (
<>
<NavBarComponent />
{this.props.navigation}
<View />
</>
);
}
}

Simple passing of Value from one Component to another through context

I'm new to react native and would like to use Context to keep a socket connection alive between screens in the future. For now, I tried to learn the concept of context just to pass simple values around but the value doesn't get sent.
Tried to follow the tutorial here, but by sending simple values instead.
I create my ValueContext in ValueContext.js here.
import React from 'react';
const ValueContext = React.createContext();
export default ValueContext;
Here's my LoginScreen.js where I set context provider.
import React, { Component } from 'react';
import ConnectionScreen from './ConnectionScreen';
import ValueContext from './ValueContext';
const testValue = 5;
export const sendValue = props => (
<ValueContext.Provider value={testValue}>
<ConnectionScreen />
</ValueContext.Provider>
)
class LoginScreen extends Component {
render() {
return()
}
}
Then in my ConnectionScreen.js
import React, { Component } from 'react';
import { View, Alert } from 'react-native';
import LoginScreen from './LoginScreen';
import ValueContext from './ValueContext';
export const receiveValue = props => (
<ValueContext.Consumer>
{testValue => <ConnectionScreen {...props} testValue={testValue} />}
</ValueContext.Consumer>
)
class ConnectionScreen extends Component {
showAlertValue = () => {
Alert.alert(this.props.testValue);
}
render() {
return(
<View>
{this.showAlertValue()}
</View>
)
}
}
So after setting the value in LoginScreen, I would like to access it in ConnectionScreen. All I get in my alert box is an empty box with no values. Am I doing something wrong here?

Passing props from one component to other component

I am trying to pass value "red" from index.js to box.js yet not working. Basically, I have a component that defines property of box and I want to pass background color "red" from index.js to Box.js component.
// Box.js
import React from "react";
const box = {
// here i would like to get the vlue name assign it to background
background: this.props.name,
width: "250px",
height: "250px"
// more code that defines how the box looks like here
};
export default Box;
/// index.js
import React, { Component } from "react";
import { render } from "react-dom";
import Box from "./Box";
render() {
return (
// when calling Box, I would like to pass the value red to varivable name as shown below
<Box name="red"></Box>
)
}
What am I missing?
You need to create a proper component:
// box.js
import React from "react";
const Box = (props) => {
// here i would like to get the value name assign it to background
const background = props.name;
const width = "250px";
const height = "250px";
// more code that defines how the box looks like here
return (
// jsx code goes here
);
};
export default Box;
in your second snippet, you are not using it properly
// index.js
import React from "react";
import Box from "./box"; // assuming that the file name is box.js and it is in the same folder
const BoxDisplay = (props) => {
return (
<Box name="red"/>
);
};
export default BoxDisplay;
Or if you want an actual Component:
// index.js
import React, {Component} from "react";
import Box from "./box";
export default class BoxDisplay extends Component({
constructor(props) {
super(props)
this.state = { //any initial state you want}
}
render() {
return (<Box name="red"/>)
}
});
There is some confusion in the question, please help us understand your problem in detail.
Your default export name is "card " and you are trying to import
"Box".
what do you mean by main source code?
Your index.js is not having a proper component syntax
please note that you cannot use "this.props" if you are not using a class based component or constructor rather use "props"
try changing the Box component as below:
const Box = (props) => {
return <p style={{background: props.name}}> Content </p>
}

React - Wait for complex method to finish before rendering

I'm trying to display a dashboard component, crunching a lot of data fetched from my redux store. This component takes a lot of time to render, mainly because of a single complex method.
Is it possible to render some kind of loader or placeholder while this method is processing ?
I tried doing so by using ComponentDidMount, but it seems like, because the method is part of my render() method, it will always be triggered first-hand.
Yes! Check out this tutorial.
Loader:
import React, {Component} from 'react';
const asyncComponent = (importComponent) => {
return class extends Component {
state = {
component: null
}
componentDidMount() {
importComponent()
.then(cmp => {
this.setState({component: cmp.default});
});
}
render() {
const C = this.state.component;
return C ? <C {...this.props}/> : null;
}
}
};
export default asyncComponent;
Usage:
import React from 'react';
import asyncComponent from '../../hoc/asyncComponent';
const AsyncButton = asyncComponent(() => {
return import('../Button');
});
const container = () => {
return (
<div>
<h1>Here goes an async loaded button component</h1>
<AsyncButton/>
</div>
);
};
export default container;
or check out this library.

Code Mirror will not function in my React application

I am attempting to create a Web IDE sort of like Eclipse Orion. The code editor that I plan to use is Code Mirror; the only difficulty is that I cannot get the code editor to load. Here is the error that I am encountering.
Here is the code that got me to this issue.
import React, { Component } from 'react';
import codemirror from 'codemirror';
import 'codemirror/mode/markdown/markdown';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/monokai.css';
class Editor extends Component {
componentDidMount = () => {
this.codeMirror = codemirror.fromTextArea(this.codeEditor, {
mode: 'markdown'
});
};
codeEditor = React.createRef();
render = () => (
<div>
<textarea ref={this.codeEditor} />
</div>
);
}
export default Editor;
This issue has been stated many times here, but with no solution that made sense in my situation. Thanks in advance.
This code seemed to do the trick, it was just an issue with the ref.
import React, { Component } from 'react';
import codemirror from 'codemirror';
import 'codemirror/mode/markdown/markdown';
import 'codemirror/lib/codemirror.css';
class Editor extends Component {
componentDidMount = () => {
this.codeMirror = codemirror(this.editor, {
mode: 'markdown'
});
};
ref = React.createRef();
render = () => (
<div>
<div ref={self => this.editor = self} />
</div>
);
}
export default Editor;

Resources