converting react class to functional component with refs - reactjs

I'm trying to use a class example from a stackblitz file and convert it to a functional component.
I don't understand how the ref works, or where the event and args that are being used in the onTyping function are coming from. Can anyone explain where those are defined and how I'd translate this to a functional component?
import { render } from 'react-dom';
import './index.css';
import * as React from 'react';
import { AutoCompleteComponent } from '#syncfusion/ej2-react-dropdowns';
import { SampleBase } from './sample-base';
import * as data from './dataSource.json';
export class Default extends SampleBase {
constructor() {
super(...arguments);
this.temp = 'sportsData';
// define the array of string
this.sportsData = data[this.temp];
}
onTyping(args) {
console.log(event.target.value);
}
render() {
return (<div id='combodefault' className='control-pane'>
<div className='control-section'>
<div className='col-lg-12 control-wrappers'>
<div id='default'>
<AutoCompleteComponent id="games" dataSource={this.sportsData} ref={(AutoComplete) => { this.listObj = AutoComplete; }} placeholder="e.g. Basketball" actionBegin={this.onTyping}/>
</div>
</div>
</div>
</div>);
}
}
render(<Default />, document.getElementById('sample'));

anything in the constructor will need to be translated to useState:
this.temp = 'sportsData';
// define the array of string
this.sportsData = data[this.temp];
becomes:
const[temp, setTemp] = useState('sportsData');
const[sportsData, setSportsData] = useState(data[temp]);
setTemp and setSportsData are functions that you use to set the state variable temp and sportsData respectively. For example, the following will set temp to 'NFSSportsData'.
setTemp('NFLSportsData');
As for the ref, you can use the hook useRef.
const listObj = useRef(null);
for component life cycle method componentDidMount, you can use the following convention.
useEffect(()=>{
// your code
}, [])
the empty bracket [] signifies only to run the code once when the component mounts. If you want to code listen to a state variable, and runs every time the variable changes, you can do the following:
useEffect(()=>{
// your code
}, [sportsData])
This code above will run every time state variable sportsData changes.
I don't think there's a way to extend a functional component like you are doing with SampleBase. Looking at the SampleBase class, it's just running a function in the lifecycle component componentDidMount. You can do something like the following:
rendereComplete() {
/**custom render complete function */
}
useEffect(()=>{
setTimeout(() => {
this.rendereComplete();
},[]);
To tie is all together, you have something like the following:
import './index.css';
import * as React from 'react';
import { AutoCompleteComponent } from '#syncfusion/ej2-react-dropdowns';
import * as data from './dataSource.json';
export const Default = ()=> {
const [temp, setTemp] = React.useState('sportsData');
const [sportsData, setSportsData] = useState(data[this.temp]);
const listObj = useRef(null);
const onTyping = (args)=>{
console.log('arg =', args);
}
const rendereComplete() {
/**custom render complete function */
}
useEffect(()=>{
setTimeout(() => {
rendereComplete();
},[]);
return (<div id='combodefault' className='control-pane'>
<div className='control-section'>
<div className='col-lg-12 control-wrappers'>
<div id='default'>
<AutoCompleteComponent id="games" dataSource={sportsData} ref={(AutoComplete) => { listObj = AutoComplete; }} placeholder="e.g. Basketball" actionBegin={onTyping}/>
</div>
</div>
</div>
</div>);
}

Related

How to use variable from script A in srcript B typescript?

I want to get variable from other script to build the next part of the page on the basis of this data.
This is code to get data from API:
import Axios from "axios";
import React from "react";
export default class PersonList extends React.Component {
state = {
dataURL: [], //from this variable I want get data
};
componentDidMount() {
Axios.get(
"https://g.tenor.com/v1/search?q=" +
"mems" +
"&key=" +
"MY_TENOR_API_KEY" +
"&limit=" +
"1"
).then((res) => {
this.state.dataURL = res.data;
this.setState({ dataURL });
console.log(this.state.dataURL);
});
}
render() {
return;
}
}
Here I want to dynamically import the script and try to get access to variable from other script
import { useState } from "react";
import styles from "../styles/Form.module.scss";
function Form() {
const [results, setResults] = useState();
return (
<div className={styles.container}>
<div className={styles.form}>
<input
type="button"
onClick={async (e) => {
const { value } = e.currentTarget;
const Fuse = (await import("../pages/api/tenor")).default;
const fuse = new Fuse(state); //I got there an error: "Cannot find name 'state'.ts(2304)"
setResults(fuse.search(value));
}}
/>
</div>
</div>
);
}
export default Form;
Basically, if you want to access a component's data from a different component you have a few options you can choose from.
Send that data as a prop.
(only relevant if the 2nd component is a child/grand-child/etc.. of the 1st component)
Manage a "global state" (a single source containing the app's relevant data).
This can be achieved via 3rd-party libraries (Redux / MobX / etc..)
Or even via React's built-in ContextAPI.
Use a shared hook containing the state which can then be accessed from other components.
(only relevant for functional components)
IMO, the simplest option is the 3rd, but it will require turning PersonList into a functional hook.
An example should look like this:
// Shared "PersonList" hook.
import Axios from "axios";
import React, { useState } from "react";
export function usePersonList() {
const [dataURL, setDataURL] = useState([]);
useEffect(() => {
Axios.get(
"https://g.tenor.com/v1/search?q=" +
"mems" +
"&key=" +
"MY_TENOR_API_KEY" +
"&limit=" +
"1"
).then(res => setDataURL(res.data));
}, []);
return dataURL;
}
// Form.tsx
import { useState } from "react";
import styles from "../styles/Form.module.scss";
function Form() {
const [results, setResults] = useState();
const dataURL = usePersonList();
return (
<div className={styles.container}>
<div className={styles.form}>
<input
type="button"
onClick={async (e) => {
const { value } = e.currentTarget;
const Fuse = (await import("../pages/api/tenor")).default;
const fuse = new Fuse(dataURL);
setResults(fuse.search(value));
}}
/>
</div>
</div>
);
}
export default Form;
You can try React Redux or useReducer to share variable between components.

Push state of reactive value to dependencies

I have an object: dynamicJSON that is changing. I would like to pass this object down to multiple dependencies: componentA, componentB. I also want the parts of the dependencies using the object to render when the object is changed.
I tried the useContext Hook, but received a dependency cycle error. What is the proper way to pass reactive values down to dependencies in react?
App.js
import { componetA } from "compA"
import { componetB } from "compB"
import { fetchLatestValue} from "api/fetchLatestValue"
import { useEffect } from "react"
export default function App() {
const dynamicJSON = ???;
useEffect(() => {
let timeoutId;
async function getlatestValue() {
try {
const data = await fetchLatestValue();
// update dynamicJSON here.
} catch (error) {
}
timeoutId = setTimeout(getlatestValue, 1000 * 1);
}
getlatestValue();
return () => {
clearTimeout(timeoutId);
};
}, []);
return (
<componetA />
<componetB />
);
}
compA
export default function componentA() {
const dynamicJSON = ???;
return(
<div>
{dynamicJSON.value}
</div>
)
};
Have you tried useEffect() with a dependency array? If anything in the dependency array is changed, the hook function will be triggered.
Reference: https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect
Sorry I mis-read your question, you should pass dynamicJSON into both components as a prop. Make dynamicJSON a state is also a good idea.
Rule of thumb: if a prop or state of a component is changed, then this component is rerendered.
import { ComponentA } from "compA";
import { ComponentB } from "compB";
import { useEffect, useState } from "react";
export default function App() {
const [dynamicJSON, setDynamicJSON] = useState({});
//...omit
return (
<ComponentA dynamicJSON={dynamicJSON}/>
<ComponentB dynamicJSON={dynamicJSON}/>
);
}
CompA.js
export default function ComponentA(props) {
const { dynamicJSON } = props;
return(
<div>
{dynamicJSON.value}
</div>
)
};

how to convert class component to function component?

I am new to React and I am trying to convert this code below to a function component, but it doesn't work, I have never used class components. Could anyone help me to convert it?
Thanks in advance.
import React from 'react'
import ReactDOM from 'react-dom'
import ScrollSnap from 'scroll-snap'
import './styles.css'
class App extends React.Component {
container = React.createRef()
bindScrollSnap() {
const element = this.container.current
new ScrollSnap(element, {
snapDestinationY: '90%',
}).bind()
}
componentDidMount() {
this.bindScrollSnap()
}
render() {
return (
<div ref={this.container}>
</div>
)
}
Here's what you need to do:
Replace createRef with useRef which is the functional hook to be used in functional components.
Replace componentDidMount with useEffect() with an empty array as dependency array, which basically runs that code once, on mount.
const App = () => {
const containerRef = React.useRef(null);
const bindScrollSnap = () => {
new ScrollSnap(containerRef , {
snapDestinationY: '90%',
}).bind()
}
React.useEffect(() => {
bindScrollSnap();
}, []);
return <div ref={containerRef}></div>
}

On click returns null instead of an object

It's really basic I guess. I'm trying to add onClick callback to my script & I believe I'm missing a value that would be responsible for finding the actual item.
Main script
import React from 'react';
import { CSVLink } from 'react-csv';
import { data } from 'constants/data';
import GetAppIcon from '#material-ui/icons/GetApp';
import PropTypes from 'prop-types';
const handleClick = (callback) => {
callback(callback);
};
const DownloadData = (props) => {
const { callback } = props;
return (
<>
<CSVLink
data={data}
onClick={() => handleClick(callback)}
>
<GetAppIcon />
</CSVLink>
</>
);
};
DownloadData.propTypes = {
callback: PropTypes.func.isRequired,
};
export default DownloadData;
Storybook code
import React from 'react';
import DownloadData from 'common/components/DownloadData';
import { data } from 'constants/data';
import { action } from '#storybook/addon-actions';
export default {
title: 'DownloadData',
component: DownloadData,
};
export const download = () => (
<DownloadData
data={data}
callback={action('icon-clicked')}
/>
);
So right now with this code on click in the storybook I'd get null and I'm looking for an object.
One of the potential issues I can see is that your handleClick function is stored as it is in-memory, when you import the component. That means you're keeping reference of something that doesn't exists and expects to use it when rendering the component with the callback prop.
Each instance of a component should have its own function. To fix it, move the function declaration inside the component. Like this:
const Foo = ({ callback }) => {
// handleClick needs to be inside here
const handleClick = callback => {
console.log("clicked");
callback(callback);
};
return <div onClick={() => handleClick(callback)}>Click me!</div>;
};
Check this example.
If this doesn't fix your problem, then there is something wrong with how you're implementing Storybook. Like a missing context.

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.

Resources