Reputation: 1465
I am converting Class based to Function Based React Component but for some reason I am having trouble achieving the same result. I have a suspicion that it might be related to this.ref
. Here is the code to let me know what I am doing wrong. Here is the link to codesandbox.
Here is the error I am getting:
Class based component
import React, { Component } from 'react';
import { getSymbolData } from './utils.js';
import AsyncSelect from 'react-select/async';
class MyAsyncSelect extends Component {
/* Select component reference can be used to get currently focused option */
getFocusedOption() {
return this.ref.select.select.state.focusedOption;
}
// we'll store lastFocusedOption as instance variable (no reason to use state)
componentDidMount() {
this.lastFocusedOption = this.getFocusedOption();
}
// Select component reference can be used to check if menu is opened */
isMenuOpen() {
return this.ref.select.state.menuIsOpen;
}
// This function will be called after each user interaction (click, keydown, mousemove).
// If menu is opened and focused value has been changed we will call onFocusedOptionChanged
// function passed to this component using props. We do it asynchronously because onKeyDown
// event is fired before the focused option has been changed.
onUserInteracted = () => {
Promise.resolve().then(() => {
const focusedOption = this.getFocusedOption();
if (this.isMenuOpen() && this.lastFocusedOption !== focusedOption) {
this.lastFocusedOption = focusedOption;
this.props.onFocusedOptionChanged(focusedOption);
}
});
};
toggleClearable = () =>
this.setState({ isClearable: !this.props.isClearable });
onInputChange = (_, { action }) => {
if (action === 'set-value') {
this.props.onOptionSelected(this.getFocusedOption());
}
};
loadOptions = async (inputText) => {
const symbolData = await getSymbolData(inputText);
console.log('symbolDATA: ', symbolData);
return symbolData;
};
// we're setting onUserInteracted method as callback to different user interactions
render() {
console.log('RENDER Child');
console.log('Props MyAsyncSelect', this.props);
return (
<div onMouseMove={this.onUserInteracted} onClick={this.onUserInteracted}>
<AsyncSelect
{...this.props}
ref={(ref) => (this.ref = ref)}
onKeyDown={this.onUserInteracted}
onInputChange={this.onInputChange}
loadOptions={this.loadOptions}
autoFocus
noOptionsMessage={() => 'Search symbol'}
placeholder="Search Symbol"
isClearable={this.props.isClearable} // allows us to clear the selected value either using the backspace button or the “x” button on the right side of the field
clear // Removing all selected options using the clear button
pop-value // Removing options using backspace
loadingIndicator
/>
</div>
);
}
}
export default MyAsyncSelect;
Function based component
import React, { useCallback, useEffect, useRef } from 'react';
import { getSymbolData } from './utils.js';
import AsyncSelect from 'react-select/async';
const MyAsyncSelect = (props) => {
// let myRef = useRef();
let myRef = React.createRef();
const myRefLastFocusedOption = useRef();
// let myRefLastFocusedOption = React.createRef();
const getFocusedOption = useCallback(() => {
console.log(myRef);
// @ts-ignore
return myRef.select.select.state.focusedOption;
}, [myRef]);
const isMenuOpen = () => {
// @ts-ignore
return myRef.select.state.menuIsOpen;
};
const onUserInteracted = () => {
Promise.resolve().then(() => {
const focusedOption = getFocusedOption();
if (isMenuOpen() && myRefLastFocusedOption.current !== focusedOption) {
myRefLastFocusedOption.current = focusedOption;
props.onFocusedOptionChanged(focusedOption);
}
});
};
// const toggleClearable = () => setIsClearable(!isClearable);
const onInputChange = (_, { action }) => {
if (action === 'set-value') {
props.onOptionSelected(getFocusedOption());
}
};
const loadOptions = async (inputText) => {
const symbolData = await getSymbolData(inputText);
console.log('symbolDATA: ', symbolData);
return symbolData;
};
useEffect(() => {
myRefLastFocusedOption.current = getFocusedOption();
// console.log('props', props);
// return () => {
// cleanup;
// };
}, [getFocusedOption, myRefLastFocusedOption]);
return (
<div onMouseMove={onUserInteracted} onClick={onUserInteracted}>
<AsyncSelect
{...props}
ref={(ref) => (myRef = ref)}
// ref={myRef}
onKeyDown={onUserInteracted}
onInputChange={onInputChange}
loadOptions={loadOptions}
autoFocus
noOptionsMessage={() => 'Search symbol'}
placeholder="Search Symbol"
isClearable={props.isClearable} // allows us to clear the selected value either using the backspace button or the “x” button on the right side of the field
clear // Removing all selected options using the clear button
pop-value // Removing options using backspace
loadingIndicator
/>
</div>
);
};
export default MyAsyncSelect;
Upvotes: 0
Views: 84
Reputation: 1465
Here is the working solution based on the sugestions from @DrewReese and @3limin4t0r
import React, { useCallback, useEffect, useRef } from 'react';
import { getSymbolData } from './utils.js';
import AsyncSelect from 'react-select/async';
const MyAsyncSelect = (props) => {
const myRef = useRef();
const myRefLastFocusedOption = useRef();
const getFocusedOption = useCallback(() => {
// @ts-ignore
return myRef.current.select.select.state.focusedOption;
}, [myRef]);
const isMenuOpen = () => {
// @ts-ignore
return myRef.current.select.state.menuIsOpen;
};
const onUserInteracted = () => {
Promise.resolve().then(() => {
const focusedOption = getFocusedOption();
if (isMenuOpen() && myRefLastFocusedOption.current !== focusedOption) {
myRefLastFocusedOption.current = focusedOption;
props.onFocusedOptionChanged(focusedOption);
}
});
};
const onInputChange = (_, { action }) => {
if (action === 'set-value') {
props.onOptionSelected(getFocusedOption());
}
};
const loadOptions = async (inputText) => {
const symbolData = await getSymbolData(inputText);
console.log('symbolDATA: ', symbolData);
return symbolData;
};
useEffect(() => {
myRefLastFocusedOption.current = getFocusedOption();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div onMouseMove={onUserInteracted} onClick={onUserInteracted}>
<AsyncSelect
{...props}
ref={myRef}
onKeyDown={onUserInteracted}
onInputChange={onInputChange}
loadOptions={loadOptions}
autoFocus
noOptionsMessage={() => 'Search symbol'}
placeholder="Search Symbol"
isClearable={props.isClearable} // allows us to clear the selected value either using the backspace button or the “x” button on the right side of the field
clear // Removing all selected options using the clear button
pop-value // Removing options using backspace
loadingIndicator
/>
</div>
);
};
export default MyAsyncSelect;
Upvotes: 3