Convert react class to react function - reactjs

Hi so I have two files and i want to convert one of them into a react functional component. The first one is PuzzleGrid and it will call the function Grid.getMatrix()
export const PuzzleGrid = () => {
const newMatrix = GridLib.getMatrix();
}
and the next is Gridlib which is a react class :
export class GridLib {
static getMatrix() {
return Array(6).fill(Array(6).fill(0));
}
}
I want to be able to convert the class into a function, this is what i have, however i am getting an error. does anyone understand why?
export const GridLib = () => {
GridLib.getMatrix = {
Array(6).fill(Array(6).fill(0));
}
}

You can simply return the results of the getMatrix method:
export const GridLib = () => {
return Array(6).fill(Array(6).fill(0));
}
However, this will cause a problem for your PuzzleGrid component, as it depends on GridLib having a getMatrix method. You would have to simply have PuzzleGrid return a GridLib for it to work as such:
export const PuzzleGrid = () => {
return <GridLib />
}
However this pattern is a bit silly and repetetive. I feel like there must be more going on to your question, but you may have reduced it down too much and stripped away some important details. What is the larger context of this issue?

Just use a function properties to declare a "static method"
export const GridLib = () => {
return <View/> ;
}
GridLib.getMatrix = () => {
return Array(6).fill(Array(6).fill(0));
}

Related

ReacJS destruct function

I learnig reactjs (I'm from Angular, C#) and I broking my head with this....have this lib
const libA=()=>{
const fnA = () => {
return ...
}
const fnB = () => {
return ...
}
const fnC = () => {
return ...
}
return [fnA, fnB, fbC]
}
export default libA
And I consume libA this way
import { libA } from '../myLib.js'
const compB=()=>{
const [fnA, fnB, fnC] = myLib()
....
fnA()...
}
My problem is if I change order const [fnB, fnA, fnC ] is seted fns to wrong name (fnB=fnA,...). There is any that I can getr corretly fn only by name or I need put correctly name var order? My pain is if I have 10 fns I wiil need declare all vars since I use only 1, 2, or alterante vars from lib?
basically i want same thing like old way
function myLib(){
fnA: function () {return...},
fnB: function () {return...},
fnC: function () {return...},
}
myLib().fnA().....
thks
Firstly, you exported libA as a default export so you should not import them as named imports
Eg: import libA from '../myLib.js' instead of import { libA } from '../myLib.js'.
Secondly, deconstructing arrays in JS mean that sequence/order of the array items are maintained. You cannot just change the order of elements when destructuring an array.
If you want to change the order of callbacks, you should export an object instead of an array in myLib.js
const libA=()=>{
const fnA = () => {
return ...
}
const fnB = () => {
return ...
}
const fnC = () => {
return ...
}
return {fnA, fnB, fbC}
}
export default libA
And you consume the libA like
const compB=()=>{
const {fnA, fnB, fnC} = myLib()
....
fnA()...
}
PS: you have 10, 20 or more functions in that export.
You don't need to destructure the import, simply call them by .
import libA from '../myLib.js'
const compB=()=>{
....
libA()?.fnA() // conditionally call the fnA only if libA returns the function
}
I think you don't need to scope all these libs inside a function to make it work as a module. Instead take advantage of the module object itself:
// libA.js
export const fnA = () => {
return ...
}
export const fnB = () => {
return ...
}
export const fnC = () => {
return ...
}
and in the other file
// anotherFile.js
import {fnA, fnB, fnC} from '../libA.js'
libA()
libB()
{...}
Other guy already mentioned that you probably not need destructure at all so direct import would be the best solution here.
But just in sake of completeness: if we have hook(myLib does not look as a hook, but let's assume) and it returns array and we don't want to put every array's item into separate variable - say, because we don't use them all, we have two alternatives.
First, we can just avoid destructuring:
myLibCallbacks = useMyLib();
...
myLibCallbacks[4]('a', 1, 2);
Sure, does not look good with this numeric indexes. Alternative is skipping elements during destructuring:
const [fnA,,,,fnE] = useMyLib();
Sure, if we can change useMyLib return value to be an object instead of array - we definitely should do that.

Create helper function for a (click handler) function to reuse in multiple React components

For a 'back' button I've created below (onClick) handler function in my React app.
const { length: historyLength, goBack, replace } = useHistory();
const handleBack = () => {
if (historyLength > 2) {
goBack();
} else {
// History length is 2 by default when nothing is pushed to history yet
// https://stackoverflow.com/questions/9564041/why-history-length-is-2-for-the-first-page
replace(HomePage);
}
};
Then I am passing the onClick handler to my child component like: <Button onClick={handleBack}/>
I am using this handleBack function in multiple places in my React app. Is it a good approach make it e.g. a helper function and how exactly?
I also don't see any issue with the code or using it as a utility callback.
Is it a good approach make it e.g. a helper function and how exactly?
Anytime you can make your code more DRY (Don't Repeat Yourself) it's generally a good thing. My personal rule-of-thumb is if I've written the same utility code a third time I'll spend a bit of time to refactor it into a common utility (and unit test!!).
I might suggest creating a custom hook to return the back handler.
Example:
import { useHistory } from 'react-router-dom';
const useBackHandler = () => {
const history = useHistory();
const handleBack = React.useCallback(() => {
const { length: historyLength, goBack, replace } = history;
if (historyLength > 2) {
goBack();
} else {
replace(HomePage);
}
}, []);
return handleBack;
};
export default useBackHandler;
Now you have a single hook to import and use.
import useBackHandler from '../path/to/useBackHandler';
...
const backHandler = useBackHandler();
...
<button type="button" onClick={backHandler}>Back?</button>
If you are needing this function in older class components, then you'll need a way to inject the handleBack as a prop. For this you can create a Higher Order Component.
Example:
import useBackHandler from '../path/to/useBackHandler';
const withBackHandler = Component => props => {
const backHandler = useBackHandler();
return <Component {...props} backHandler={backHandler} />;
};
export default withBackHandler;
To use, import withBackHandler and decorate a React component and access props.backHandler.
import withBackHandler from '../path/to/withBackHandler';
class MyComponent extends React.Component {
...
someFunction = () => {
...
this.props.backHandler();
}
...
}
export default withBackHandler(MyComponent);
#meez
Don't see why this wouldn't work. Just a couple of things: (a) I would add the event argument and e.preventDefault() within the function and (b) would be careful of the function name you are passing on the onClick property of your button: handleBackClick !== handleBack, you'll get an ReferenceError because of an undefined function.
Additionally, I also noticed that this can be achieved with native browser functions. Here's a snippet:
const { length: historyLength, back } = window.history;
const { replace } = window.location;
const handleBack = (e) => {
e.preventDefault();
if (historyLength > 2) {
back();
} else {
replace('homepageUrl');
}
};

How to call react function from external JavaScript file

I have read this post [ https://brettdewoody.com/accessing-component-methods-and-state-from-outside-react/ ]
But I don't understand.
that is not working on my source code.
it's my tsx file
declare global {
interface Window {
fn_test(): void;
childComponent: HTMLDivElement; <-- what the... ref type????
}
}
export default function Contact(): React.ReactElement {
....
function file_input_html( i:number ): React.ReactElement {
return (
<form id={`frm_write_file_${i}`} .... </form>
)
}
....
return (
<div ref={(childComponent) => {window.childComponent = childComponent}}>
....
)
it's my external javascript file
function fn_test(){
window.childComponent.file_input_html(3)
var element = document.getElementById("ifrm_write_file");
// element.value = "mystyle";
}
How can i call file_input_html function?
plase help me ...
You have some logic here that doesn't completely make sense.
In your class, you define file_input_html, which returns a component.
Then, in fn_test, you call attempt to call that function (which doesn't work -- I'll address that in a minute), but you don't do anything with the output.
The article that you linked to tells you how to get a ref to a component (eg the div in this case) -- not the actual Contact, which doesn't have a property named file_input_html anyway -- that's just a function you declared inside its scope.
What I'm assuming you want to happen (based on the code you shared) is for your external javascript file to be able to tell your component to render a form with a certain ID and then be able to get a reference to it. Here's an example of how to do this (it's a little convoluted, but it's a funny situation):
const { useState } = React
const App = (props) => {
const [formId, setFormId] = useState(2)
useEffect(() => {
window.alterFormId = setFormId
},[])
return (<div id={"form" + formId} ref={(ourComponent) => {window.ourComponent = ourComponent}}>
Text {formId}
</div>);
}
setTimeout(() => {
window.alterFormId(8);
setTimeout(() => {
console.log(window.ourComponent)
window.ourComponent.innerText = "Test"
}, 20)
}, 1000)
ReactDOM.render(<App />,
document.getElementById("root"))
What's happening here:
In useEffect, I set alterFormId on window so that it can be used outside of the React files
Using the technique you linked to, I get a ref to the div that's created. Note that I'm setting the ID here as well, based on the state of formId
The setTimeout function at the end tests all this:
a) It waits until the first render (the first setTimeout) and then calls alterFormId
b) Then, it waits again (just 20ms) so that the next run loop has happened and the React component has re-rendered, with the new formId and reference
c) From there, it calls a method on the div just to prove that the reference works.
I'm not exactly sure of your use case for all this and there are probably easier ways to architect things to avoid these issues, but this should get you started.
안녕하세요. 자바스크립트로 흐름만 알려드리겠습니다
아래 코드들을 참고해보세요.
iframe간 통신은
window.postMessage API와
window.addEventListener('message', handler) 메시지 수신 이벤트 리스너 로 구현할 수있습니다. 보안관련해서도 방어로직이 몇줄 필요합니다(origin 체크 등)
in parent
import React from 'react';
export function Parent () {
const childRef = React.useRef(null);
const handleMessage = (ev) => {
// 방어로직들
if (check ev.origin, check ev.source, ....) {
return false;
}
console.log('handleMessage(parent)', ev)
}
React.useEffect(() => {
window.addEventListener('message', handleMessage);
// clean memory
return () => {
window.removeEventListener('message', handleMessage);
}
})
return (
<div>
<iframe ref="childRef" src="child_src" id="iframe"></iframe>
</div>
)
}
in child
import React from 'react';
export function Iframe () {
const handleMessage = (ev) => {
console.log('handleMessage(child)', ev)
}
const messagePush = () => {
window.parent.postMessage({ foo: 'bar' }, 'host:port')
}
React.useEffect(() => {
window.addEventListener('message', handleMessage);
// clean memory
return () => {
window.removeEventListener('message', handleMessage);
}
})
return (
<div>
<button onClick={messagePush}>Push message</button>
</div>
)
}

Reactjs hook that uses other hooks

I needed to create a custom hook which is supposed to contain all the handlers that will be used everywhere in my page. My requirements were;
Handlers are supposed to be accessible from all the components in the page
Handlers should be able to use other hooks, like useContext
So, created a useHandlers hook sandbox
However, couldn't make the LogHandler accessible from the page, receving LogHandler is not a function
Any idea?
The issue why you're getting LogHandler is not a function is because it's undefined and it doesn't get initialized until HandlerComp gets called:
export const userHandlers = (): IUseHandlers => {
// initialization skipped, so `LogHandler` is undefined
let LogHandler: () => void;
const HandlersComp: React.FunctionComponent<HandlersProps> = (
props: HandlersProps
) => {
// initialized here, but that's after `HandlersComp` gets caled
LogHandler = () => {
console.log("Hi from LogHandler");
};
return <></>;
};
return { HandlersComp, LogHandler };
}
I suggest you move the initialization step from HandlersComp like so:
export const useHandlers = (): IUseHandlers => {
const LogHandler: () => void = () => {
console.log("Hi from LogHandler");
};
const HandlersComp: React.FunctionComponent<HandlersProps> = (
props: HandlersProps
) => {
LogHandler()
return <></>;
};
return { HandlersComp, LogHandler };
};
Some notes:
HandlersComp looks like it should be a separate and reusable component, rather than a hook
LogHandler also looks more like a utility function, rather than a hook
LogHandler shouldn't be using PascalCase for naming as that should be "reserved" for React components; HandlersComp is fine, since it looks like it's a component

How do I update an array using the useContext hook?

I've set a Context, using createContext, and I want it to update an array that will be used in different components. This array will receive the data fetched from an API (via Axios).
Here is the code:
Context.js
import React, { useState } from "react";
const HeroContext = React.createContext({});
const HeroProvider = props => {
const heroInformation = {
heroesContext: [],
feedHeroes: arrayFromAPI => {
setHeroesContext(...arrayFromAPI);
console.log();
}
};
const [heroesContext, setHeroesContext] = useState(heroInformation);
return (
<HeroContext.Provider value={heroesContext}>
{props.children}
</HeroContext.Provider>
);
};
export { HeroContext, HeroProvider };
See above that I created the context, but set nothing? Is it right? I've tried setting the same name for the array and function too (heroesContex and feedHeroes, respectively).
Component.js
import React, { useContext, useEffect } from "react";
import { HeroContext } from "../../context/HeroContext";
import defaultSearch from "../../services/api";
const HeroesList = () => {
const context = useContext(HeroContext);
console.log("Just the context", context);
useEffect(() => {
defaultSearch
.get()
.then(response => context.feedHeroes(response.data.data.results))
.then(console.log("Updated heroesContext: ", context.heroesContext));
}, []);
return (
//will return something
)
In the Component.js, I'm importing the defaultSearch, that is a call to the API that fetches the data I want to push to the array.
If you run the code right now, you'll see that it will console the context of one register in the Just the context. I didn't want it... My intention here was the fetch more registers. I have no idea why it is bringing just one register.
Anyway, doing all of this things I did above, it's not populating the array, and hence I can't use the array data in another component.
Does anyone know how to solve this? Where are my errors?
The issue is that you are declaring a piece of state to store an entire context object, but you are then setting that state equal to a single destructured array.
So you're initializing heroesContext to
const heroInformation = {
heroesContext: [],
feedHeroes: arrayFromAPI => {
setHeroesContext(...arrayFromAPI);
console.log();
}
};
But then replacing it with ...arrayFromAPI.
Also, you are not spreading the array properly. You need to spread it into a new array or else it will return the values separately: setHeroesContext([...arrayFromAPI]);
I would do something like this:
const HeroContext = React.createContext({});
const HeroProvider = props => {
const [heroes, setHeroes] = useState([]);
const heroContext = {
heroesContext: heroes,
feedHeroes: arrayFromAPI => {
setHeroes([...arrayFromAPI]);
}
};
return (
<HeroContext.Provider value={heroContext}>
{props.children}
</HeroContext.Provider>
);
};
export { HeroContext, HeroProvider };

Resources