how to convert class component to function component? - reactjs

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>
}

Related

converting react class to functional component with refs

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>);
}

how to pass value from parent functional component to child class component in react

Hi all I have following code:
Parent functional component
import React, { useEffect, useState } from "react";
function Parent() {
const [apiData, setApiData] = useState({});
async function fetchData() {
const res = await fetch("some API data");
res.json().then((res) => setApiData(res));
}
useEffect(() => {
fetchData();
}, []);
return (<div><Child title={apiData.title} /></div>);
}
export default Parent;
And have following code for my Child class component:
import React from "react";
class Child extends React.Component {
render() {
return (<div> "here should be title from API" </div> )
}
}
I can successfully fetch my API data, but I have some difficulties to pass to my Child component. Please help me. Thanks.
You should use the same, as you use it in the functional component (+ adding "this." before the props).
import React from "react";
class Child extends React.Component {
render() {
return (<div>{this.props.title}</div> )
}
}
See more examples here:
https://reactjs.org/docs/components-and-props.html

Trying to use a function that is in my context provider file

I am using Context-Api and am trying to use a function provided from my file in a lifecycle method. the function isnt wrapped in a consumer of course so i looked at the documentation and set value to context. this still isnt working.Everyting is working in my return of my class component but component did mount does not work.
import { ProductConsumer } from '../context';
export default class Details1 extends Component
componentDidMount() {
let value = this.context;
let id = this.props.match.params.id;
value.handleDetail(id);
}
render() {
{value => {
const {
id,...} = value.detailProduct;
return (
<ProductConsumer>
{value => {
My Component
</ProductConsumer>
export const Details = () => (
<Product.Consumer>
{context =>
<Details1 context={context}/>
}
</Product.Consumer>
)
You can either wrap the component with the consumer, passing it the function as a prop, or (better - ) convert your component to a functional component, using the useContext hook to get the values from your context.
import React, { useContext } from "react";
import someContext from "./context-path";
const MyComponent = () => {
const { myFunction } = useContext(someContext);
...
};

Access to component methods in React

I'm using a component called react-input-autosize. The issue I'm facing is that it won't resize the input on viewport resize, so I wan't to manually hook into the component methods and run copyInputStyles() and updateInputWidth().
Pretty new to React so don't know how to achieve this. You can expose the input via the inputRef, but that doesn't really help me no?
My current reduced test case looks like this, would be happy with any pointers on how to run the component methods.
import React from 'react';
import styled from '#emotion/styled';
import AutosizeInput from 'react-input-autosize';
import {throttle} from 'lodash';
const StyledAutosizeInput = styled(AutosizeInput)`
max-width: 100vw;
`;
export default class signUp extends React.Component {
constructor (props) {
super(props);
this.inputRef = React.createRef();
}
componentDidMount() {
console.log(this.inputRef); // outputs input node
window.addEventListener('resize', this.resize);
this.resize();
}
componentWillUnmount() {
window.removeEventListener('resize', this.resize);
}
resize = throttle(() => {
console.log('force resize');
}, 100);
render() {
return (
<StyledAutosizeInput
inputRef={node => this.inputRef = node}
/>
);
}
}
The inputRef={node => this.inputRef = node} callback is referring to the html input element and not the AutosizeInput component. Pass the ref via the ref prop to access the component's methods.
...
resize = throttle(() => {
if (this.inputRef.current) {
this.inputRef.current.copyInputStyles()
this.inputRef.current.updateInputWidth()
}
}, 100);
render() {
return (
<StyledAutosizeInput
ref={this.inputRef}
/>
);
}

How to get value of react app state['Key'] in test?

I'm trying to make an test for a react application, but I have run into a problem. I couldn't get jest to work properly so I have been trying to work around it which has caused my current problem. The reason my test isn't working is because the way I'm calling for the values of the keys in state requires me to be using enzyme in jest. Is their a way to get the values of the keys in state inside the react app without using jest and how do I do it?
this is the function in my react app:
setTimeMonth = (time) => {
const today = moment().format('YYYY-MM-DD');
const before = moment().subtract(time, 'months').format('YYYY-MM-DD');
this.setState({Date2: today});
this.setState({Date1: before});
}
this is the test for the function:
it('setTimeMonth(number)', () => {
const wrapper = new ReactPage;
expect(wrapper.state('Date1').toMatch(""));
expect(wrapper.state('Date2').toMatch(""));
wrapper.setTimeMonth(1);
expect(wrapper.state('Date1').toMatch(moment().format('YYYY-MM-DD')));
expect(wrapper.state('Date2').toMatch(moment().subtract(1, 'Month').format('YYYY-MM-DD')));
});
Here is a working example:
example.js:
import * as React from 'react';
import moment from 'moment';
export class Example extends React.Component{
constructor(props) {
super(props);
this.state = { Date1: '', Date2: '' };
}
setTimeMonth = (time) => {
const today = moment().format('YYYY-MM-DD');
const before = moment().subtract(time, 'months').format('YYYY-MM-DD');
this.setState({Date2: today});
this.setState({Date1: before});
};
render() {
return <div/>
}
}
example.test.js:
import * as React from 'react';
import { shallow } from 'enzyme';
import moment from 'moment';
import { Example } from './example';
describe('Example', () => {
it('setTimeMonth(number)', () => {
const wrapper = shallow(<Example/>);
expect(wrapper.state('Date1')).toBe('');
expect(wrapper.state('Date2')).toBe('');
wrapper.instance().setTimeMonth(1);
expect(wrapper.state('Date2')).toMatch(moment().format('YYYY-MM-DD'));
expect(wrapper.state('Date1')).toMatch(moment().subtract(1, 'months').format('YYYY-MM-DD'));
});
});

Resources