I am trying to use a vanilla js library in react. since there is a lot of code, I wondered how to split it. Currently, I'm storing the code for each function in separate files... but I'm not sure how to import and run it. Is there a way to run this code in the useEffect hook or will I have to convert these old classes to es6?
editor.js
EditorUi = function (editor, container, lightbox) {
this.destroyFunctions = [];
this.editor = editor || new Editor();
this.container = container || document.body;
};
EditorUi.compactUi = true;
EditorUi.prototype.splitSize = 8;
MyComp.jsx
import React from 'react';
import EditorUi from '~/config/functions/bpm/EditorUi';
export default function MyComp() {
const divGraph = React.useRef(null);
React.useEffect(() => {
// ...?
});
return <div style={{ width: '100%', height: '80vh' }} ref={divGraph} />;
}
You've to export a function from the editor js file, then you can use it in useEffect in your component
Related
When I used the same method in another project then It was working well but I decided to use the same method for my current project then I am having an issue with the given below
react-dom.development.js:14724 Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See some tips for tips about how to debug and fix this problem.
at Object.throwInvalidHookError (react-dom.development.js:14724:13)
at useState (react.development.js:1497:21)
Below is my first component name is GetWindowWidth.js. this component is related to apply the screen with for desktop, tab, and mob screen
GetWindowWidth.js
import {useState,useEffect} from "react";
const GetWindowWidth = () => {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
window.addEventListener("resize", updateWidth);
return () => window.removeEventListener("resize", updateWidth);
});
const updateWidth = () => {
setWidth(window.innerWidth);
};
return width;
}
export default GetWindowWidth;
Below is another component where I am trying to call the above component to apply the screen width.
AnotherComponent.js
import React, { Component } from 'react'
import GetWindowWidth from './GetWindowWidth';
export class AnotherComponent extends Component {
render() {
const width = GetWindowWidth();
return (
<div color={width<600? '#161625':"#f3990f"}>UserCatalogTwo</div>
)
}
}
export default AnotherComponent
I don't why this is coming even it's working on other projects.
GetWindowWidth is a hook, not a component, since it doesn't render anything. (And for that reason its name should start with use.) You can't use hooks in class components. You'll have to either rewrite the class component as a function component, or write a non-hook version of GetWindowWidth.
For instance, you might have a module with a function that sets up the resize handler:
// watchwidth.js
export function watchWidth(callback) {
const handler = () => {
callback(window.innerWidth);
};
window.addEventListener("resize", handler);
return () => {
window.removeEventListener("resize", handler);
};
}
...then import it:
import { watchWidth } from "./watchwidth.js";
...and use it in your class component:
componentDidMount() {
this.stopWatchingWidth = watchWidth(width => this.setState({width}));
}
componentWillUnmount() {
this.stopWatchingWidth();
}
That's off-the-cuff and untested, but gives you an idea.
Say I create a simple React context to check if I am connected
import NetInfo, { NetInfoState } from '#react-native-community/netinfo';
import Constants, { AppOwnership } from 'expo-constants';
import React, { PropsWithChildren, createContext, useContext, useEffect, useState } from 'react';
import { Platform } from 'react-native';
const ConnectionContext = createContext<boolean>(undefined);
export function ConnectionProvider({ children }: PropsWithChildren<any>): JSX.Element {
const [connected, setConnected] = useState(false);
function handleConnectionChange(netInfo: NetInfoState) {
setConnected(
(Platform.OS === 'ios' && Constants.appOwnership === AppOwnership.Expo) ||
(netInfo.isConnected && (netInfo.isInternetReachable ?? true))
);
}
useEffect(() => {
const subscriptionCancel = NetInfo.addEventListener(handleConnectionChange);
return () => subscriptionCancel();
}, []);
return <ConnectionContext.Provider value={connected}>{children}</ConnectionContext.Provider>;
}
export function useConnection() {
return useContext(ConnectionContext);
}
I was wondering if I want to use it in my existing component XYZ, is there a less roundabout way of doing it than the following
From:
export function XYZ() {
...xyz code...
}
to:
export function XYZ() {
return (
<ConnectionContextProvider>
<RealXyz>
</ConnectionContextProvider>
);
}
function RealXyz() {
const connected = useConnection();
...xyz code...
}
I don't think context is really necessary for this since a connection is more of a singleton type of thing. The following code should be in its own file, and you can import this hook anywhere in your app.
let _isConnected = false;
export const useConnection = () => {
const [isConnected, setConnected] = useState(_isConnected);
useEffect(() => {
function handleConnectionChange(netInfo: NetInfoState) {
_isConnected = (Platform.OS === 'ios' && Constants.appOwnership === AppOwnership.Expo) ||
(netInfo.isConnected && (netInfo.isInternetReachable ?? true))
setConnected(_isConnected);
}
const subscriptionCancel = NetInfo.addEventListener(handleConnectionChange);
return () => subscriptionCancel();
}, []);
return isConnected;
}
Explanation:
Let's say you have two components which use this hook. When your app first renders, only ComponentA is mounted. Some time later the connection state changes to true. Then some time later ComponentB is mounted. We want ComponentB to know that the connection state is currently true, which is why we use the singleton pattern (eg. private global variable _isConnected). It doesn't matter much that there are multiple event listeners as those are cheap and get removed when the component is unmounted.
Context is handy if you have data that needs to be shared across multiple components and you do not want to pass it down the tree by props.
from the docs:
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
In your example I would use useState, but to give you a good idea where you could opt for context check the following snippet:
...
function ABC() {
const connected = useConnection();
...abc code...
}
function ABCParent() {
return <ABC />
}
...
function XYZ() {
const connected = useConnection();
...xyz code...
}
function XYZParent() {
return <XYZ />
}
...
function App() {
return (
<ConnectionContextProvider>
<ABCParent />
<XYZParent />
</ConnectionContextProvider>
)
}
The two components that make use of the context are "deep" in the tree and in separate branches. The example is a bit simple and you could easily pass the data that you need through props and still have a maintainable code base.
But ultimately if you feel that your data model can be "global" and you have enough dependents in separate branches or in the same branch in multiple levels go for context API.
Some data model examples where I find the use of context API useful are theme, app settings, routing and translations.
One thing to note: The components that depend on a context will be less reusable (This is more relevant across projects) and sometimes you can opt for composition instead of using the context API. Check the before you use context section of the docs for more information about this.
This is a Lazy Route component I wrote a while ago (code below).
This is what it does:
It initially render a lazyLoading = true state to show some spinner or something.
Then on useEffect it will dynamic import() a component module and set the LazyComponent state. Like setLazyComponent(module)
Then it will turn the loading to false and render <LazyComponent.default />
Note: It works as intended.
My question is:
I've tried to do the following:
Set the default property to the state. As in setLazyComponent(module.default). Since SomeComponent, which is the component that is being lazy loaded has a single default export.
Then I should be able to render just <LazyComponent/> instead of <LazyComponent.default/>
But it does not work.
And I get this error:
Why does it not work? All the rest of the code is the same. The only change I'm trying to make is the place where I access the default property.
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
const LS = {};
LS.Container_DIV = styled.div`
`;
async function lazyRender() {
const module = await import("./SomeComponent");
return new Promise((resolve) => {
resolve(module);
});
}
function LazyRoute(props) {
console.log('Rendering LazyRoute...');
const [lazyLoading,setLazyLoading] = useState(true);
const [LazyComponent,setLazyComponent] = useState(null);
useEffect(() => {
async function getLazyComponent() {
const module = await lazyRender();
setLazyComponent(module);
setLazyLoading(false);
}
getLazyComponent();
},[]);
return(
lazyLoading ?
<div>I am Lazy Loading....</div>
: <LazyComponent.default {...props}/>
);
}
export default React.memo(LazyRoute);
I'm trying to call a vanilla javascript class inside a component.
This vanilla class is a distributor of cards and i wanted to separate the cards distribution logic from the component.
Where should I instanciate my vanilla class ?
How sure am I of the integrity of this instance (like when the components update) ?
I tried some things like putting in it inside useEffect(()=>{},[]) when the components mount but it didn't work (i didn't have access to my instance), I found this way but it works partially :
import * as WebBrowser from 'expo-web-browser';
import React, { useState, useEffect } from 'react';
import {
Image,
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
Button,
} from 'react-native';
import BasicMonoSlide from '../components/BasicMonoSlide';
import BasicMonoSlideDistributor from '../models/BasicMonoSlideDistributor';
export default function GameScreen(props) {
const [currentSlide, setCurrentSlide] = useState({});
const [joueurs,setJoueurs] = useState(
JSON.parse(JSON.stringify(props.joueurs))
);
var basicMonoSlideDistributor = new BasicMonoSlideDistributor();
useEffect(()=>{
console.log(currentSlide);
setCurrentSlide(getBasicMonoSlide());
},[]);
useEffect(()=>{
console.log(joueurs);
},[joueurs])
const nextSlide = () => {
console.log("appel next slide");
setCurrentSlide(getBasicMonoSlide());
};
const getBasicMonoSlide = ()=>{
console.log("appel getBasicMonoSlide");
var newSlideData = basicMonoSlideDistributor.getNewSlideData(joueurs,modifyJoueurs,()=>{nextSlide();});
console.log(newSlideData[2]);
return {type:'basicMonoSlide',slide:<BasicMonoSlide questionText={newSlideData[0]} btnText={newSlideData[1]} btnClickHandler={newSlideData[2]}/>};
};
const modifyJoueurs = (index,nom,sexe,orientation,enCoupleAvec,score) => {
var joueursActuel = joueurs;
console.log("modif du joueur a l'index "+index+" "+nom+","+sexe+","+orientation+","+enCoupleAvec+","+score);
const newJoueursArray = joueurs.map((item, indexMap) => {
if (indexMap === index) {
item.index=index;
item.nom=nom;
item.sexe = sexe;
item.orientation=orientation;
item.enCoupleAvec=enCoupleAvec;
item.score=score;
return item;
} else {
return item;
}
});
setJoueurs(newJoueursArray);
}
return (
<View style={styles.container}>
{currentSlide.slide||null}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
My "newSlideData[2]", the onPress given to my slide that has a button is an arrow function defined in my class BasicMonoSlideDistributor calling an arrow function defined in my component and when i click it works 1 or 2 times then does nothing
I know it's long, maybe just answer the first questions :)
probably the slide is attached to the DOM but you donĀ“t see this because you have problems with the CSS, check if in the element is add to the DOM (with the dev tools in your browser) and if is attached, search the problem in the CSS.
Is there any way to move the jsx from a component's render function to a separate file? If so, how do I reference the jsx in the render function?
You can use react-templates. It gives you exactly this separation between the markup and the component itself, and much more.
I found it very useful for my needs (a large scale web app).
One problem with moving templates into a separate file is that if you use handlers like:
var myTemplate = (
<form onSubmit={this.handleSubmit}></form>
);
and then in your component you use:
render: function() {
return myTemplate;
}
the generated template code will call this.handleSubmit(), so the "this" will be wrong and the handlers won't work. What you need to do is put them in a function, like this:
var myTemplate = function() {
return (
<form onSubmit={this.handleSubmit}></form>
);
};
then in your component's render function, you need to bind it to 'this' correctly, then call it, like this:
render: function() {
return myTemplate.bind(this)();
},
Now you can put that template definition anywhere, in a separate file or however you want to structure and reference your own code. (power to you! Don't listen to these crazy prescriptive frameworks! :) )
Here is a pattern for separating the template jsx that uses CommonJS modules in NodeJS, Browserify or Webpack. In NodeJS, I found the node-jsx module helpful to avoid the need to compile the JSX.
// index.js
require('node-jsx').install({extension: '.jsx'});
var React = require('react'),
Component = require('./your-component');
// your-component.jsx
var YourComponent,
React = require('react'),
template = require('./templates/your-component.jsx');
module.exports = YourComponent = React.createClass({
render: function() {
return template.call(this);
}
});
// templates/your-component.jsx
/** #jsx React.DOM */
var React = require('react');
module.exports = function() {
return (
<div>
Your template content.
</div>
);
};
Update 2015-1-30: incorporated suggestion in Damon Smith's answer to set this in the template function to the React component.
Update 12/2016: the current best practice is to use the .js extension and use a build tool like Babel to output the final javascript from your source. Take a look at create-react-app if you're just getting started. Also, the latest React best practices do recommend a separation between components that manage state (typically called "container components") and components that are presentational. These presentational components can now be written as functions, so they are not far off from the template function used in the previous example. Here is how I would recommend decoupling most of the presentational JSX code now. These examples still use the ES5 React.createClass() syntax.
// index.js
var React = require('react'),
ReactDOM = require('react-dom'),
YourComponent = require('./your-component');
ReactDOM.render(
React.createElement(YourComponent, {}, null),
document.getElementById('root')
);
// your-component.js
var React = require('react'),
YourComponentTemplate = require('./templates/your-component');
var YourComponentContainer = React.createClass({
getInitialState: function() {
return {
color: 'green'
};
},
toggleColor: function() {
this.setState({
color: this.state.color === 'green' ? 'blue' : 'green'
});
},
render: function() {
var componentProps = {
color: this.state.color,
onClick: this.toggleColor
};
return <YourComponentTemplate {...componentProps} />;
}
});
module.exports = YourComponentContainer;
// templates/your-component.js
var React = require('react');
module.exports = function YourComponentTemplate(props) {
return (
<div style={{color: props.color}} onClick={props.onClick}>
Your template content.
</div>
);
};
I just separated JSX into anonymous function files
template.js
export default (component) => {
return <h1>Hello {component.props.name}</h1>
}
my-component.js
import React, {Component} from 'react';
import template from './template';
export default MyComponent extends Component {
render() {
return template(this);
}
}
In template you can access props or state or functions using component variable.
If you don't use any module system, i.e. rely on script tags only, simple expose your JSX component in a global variable and use it when you need :
// component.js
var Component = React.createClass({ /* your component */ });
// main.js
React.renderComponent(Component({}), domNode);
Note : the script tag for component.js must appear before the script tag for main.js.
If you use a Commonjs-like module system like Browserify, simply export your component definition and require it when you need it.
// component.js
var React = require("react");
module.exports = React.createClass({ /* your component */ });
// main.js
var Component = require("component.js");
React.renderComponent(Component({}), domNode);