Using optionRenderer with Select.Aysnc (react-select) - javascript

I can't find in the documentations how to use optionRenderer prop with react-select async (Select.Async)
here is a question that already been answered, but the renderOptions is not being called at all
here is my a snippet of my code:
renderOption = (option) => {
return (
<div>
// for example:
{option.label}: {option.value}
</div>
)
}
<Select.Async
placeholder={placeholder}
loadOptions={(inputValue) => this.asyncLoadOptions(inputValue)}
isClearable
onChange={(value, e) => {
this.onDropDownFilterChange(value, e)
}}
onMenuScrollToBottom={this.fetchDropDownNextPage}
defaultOptions={defaultOptions}
defaultValue={defaultValue}
styles={this.props.hasError ? customStyles : undefined}
optionRenderer={this.renderOption}
/>
Here I want to keep the functioality and styles for each dropDown item as is (e.g onMouseOver and so on) I just want to format how the items are shown in the list, so is this the right way to do it? or there no other option than using the components prop.
I was able to achieve the formatting right using the components prop, but I lost the styles, and all the mouse events.

so for those who are looking for the answer, this is what I ended up using and it does the job: (unrelated code has been removed to keep the snippet concise)
import AsyncSelect from 'react-select/async';
import { components } from 'react-select';
const Option = props => {
return (
<components.Option {...props} >
{props.data.label}<br/>
<small style={{color: 'gray'}}>
{props.data.manufacturerName || 'Unknown'} | {props.data.assetGroupDescription || 'Unknown'}
</small>
</components.Option>
)};
class FilterDropDownMenu extends Component {
render() {
return (
<>
<label htmlFor={id}>{translate(placeholder)}</label>
<AsyncSelect
{...this.props}
isClearable
onChange={(value, e) => {
this.onDropDownFilterChange(value, e)
}}
onMenuScrollToBottom={this.fetchDropDownNextPage}
defaultOptions={defaultOptions}
defaultValue={defaultValue}
styles={hasError ? customStyles : undefined}
components={{ Option }}
/>
</>
)
}
}
This way, I get the formatting that I want, And I don't loose the built in functionality of react-select (mouse events and styling and so on).

Related

Prevent a component rendering inside formik

I want to disable the rendering of a component inside react.js formik library
here is an example of code structure I have currently
<formik
initialValue={{
"show":false
}}>
return (
<button name="showbtn" onclick={setFieldValue("show",true)}/>
{values?.show ?
(
<Text>Hello</Text>
) :
null}
<Rerenderedcomponent /> //no prop passed here
)
</formik>
And here is an example of my Rerendered component file
function Rerenderedcomponent()
{
const callingAPI = useCallback(()=>response,[])
}
export default React.memo(Rerenderedcomponent)
Now as I am clicking on the button(name showbtn) formik "show" field value is getting updated but my component(Rerenderedcomponent) is also getting rerendered & hence the api in it is getting called again
I tried by setting enableReinitialize={false} but nothing works
Is it possible to prevent this rerendering of the component(Rerenderedcomponent) on formik field update
PS:- The component should remain inside formik tag only
I prevent the component rerendering inside formik using the below workaround:
Created a new component say (Hello.js) & included the conditonal rendering(that was inside formik tag previously) inside it, like an example shown below
function Hello({show})
{
return(
<>
{show && <Text>Hello</Text>}
</>
)
}
export default React.memo(Hello);
Now I just imported & use the Hello.js component inside formik as shown below
<formik
initialValue={{
"show":false
}}>
return (
<button name="showbtn" onclick={setFieldValue("show",true)}/>
<Hello show={values?.show}/> // Hello.js component
<Rerenderedcomponent /> //this will not rerender now
)
</formik>
Now since the component is already mounted into the DOM the rerendering will not occur on show value change
Also there is one another workaround to resolve this issue just by changing the order of components inside formik tag
<formik
initialValue={{
"show":false
}}>
return (
<button name="showbtn" onclick={setFieldValue("show",true)}/>
<Rerenderedcomponent /> //placed above conditional rendering
{ values?.show ?
(
<Text>Hello</Text>
) :
null
}
)
I moved the rerendered component above the conditional rendering & it resolved the issue
To prevent RerenderedComponent from contacting the api every time. You must define a state in the parent component and pass it to child component:
const [apiData, setApiData] = useState(); // <===
return (
<Formik
initialValues={{ show: false }}
onSubmit={(values) => {}}
>
{({ setValues, values }) => (
<Form>
<button
type="button"
onClick={() => setValues({ show: !values.show })}
>
{values.show ? "hide" : "show"}
</button>
{values.show && (
<Rerenderedcomponent apiData={apiData} setApiData={setApiData} /> // <===
)}
</Form>
)}
</Formik>
);
And in the child component, you can check the existence of apiData and communicate with the api if needed:
function Rerenderedcomponent({ apiData, setApiData }) {
useEffect(() => {
if (!apiData) {
// fetch data here ...
setApiData('<response>');
}
}, []);
return null; // A Redact component must return a value
}

React - Access the state of a parent from the children, without nested function

Hello,
I am coming to you today for the first time because I have not found a solution to my problem.
I have been using react for a few weeks, Don't be too cruel about the quality of my code 😁.
Problem :
I am looking to access the state of a parent from their children.So I want to be able to access the setHeight function and the height variable for example from a child component.
Please note :
However, to keep some flexibility, I don't want to have any Components inside our.
I looked at redux to be able to do this, but the problem is that the data is global so creating multiple dropdowns would not be possible.
(Unless I didn't understand too much, redux is quite complex)
Diagram :
I have created a diagram to explain it a little better.,
I'd like the children of DropdownMenu to be able to access the state of the latter, Also, the different Dropdowns must have their own state independently.
So ideally I want to keep the same structure as find very flexible, and the possibility to create several dropdown.
Code :
I Share my four components :
export default function Navbar () {
return (
<nav className={styles.navbar}>
<ul className={styles.navbarNav}>
<NavItem icon={<NotificationsIcon />} />
<NavItem icon={<AccessTimeFilledIcon />} />
<NavItem icon={<FileOpenIcon />}>
<DropdownMenu>
<DropdownSubMenu menuName="Home">
<DropdownItem>My Profile</DropdownItem>
<DropdownItem leftIcon={<AccessTimeFilledIcon />} rightIcon={<ChevronRightIcon />} goToMenu="pages">Pages</DropdownItem>
<DropdownItem>IDK</DropdownItem>
<DropdownItem>Test</DropdownItem>
</DropdownSubMenu>
<DropdownSubMenu menuName="pages">
<DropdownItem>Pages</DropdownItem>
<DropdownItem leftIcon={<AccessTimeFilledIcon />} rightIcon={<ChevronRightIcon />} goToMenu="home">Home</DropdownItem>
</DropdownSubMenu>
</DropdownMenu>
<DropdownMenu>
<DropdownSubMenu menuName="config">
<DropdownItem>Foo</DropdownItem>
<DropdownItem leftIcon={<AccessTimeFilledIcon />} rightIcon={<ChevronRightIcon />} goToMenu="theme">Configuration</DropdownItem>
<DropdownItem>Bar</DropdownItem>
<DropdownItem>Baz</DropdownItem>
</DropdownSubMenu>
<DropdownSubMenu menuName="theme">
<DropdownItem>Hi StackOverflow</DropdownItem>
<DropdownItem leftIcon={<AccessTimeFilledIcon />} rightIcon={<ChevronRightIcon />} goToMenu="config">Theme</DropdownItem>
</DropdownSubMenu>
</DropdownMenu>
</NavItem>
</ul>
</nav>
);
};
type Props = {
children?: React.ReactNode | React.ReactNode[];
leftIcon?: React.ReactNode | JSX.Element | Array<React.ReactNode | JSX.Element>;
rightIcon?: React.ReactNode | JSX.Element | Array<React.ReactNode | JSX.Element>;
goToMenu?: string;
goBack?: boolean;
OnClick?: () => void;
};
export default function DropdownItem({ children, leftIcon, rightIcon, goToMenu, goBack, OnClick }: Props) {
const handleClick = OnClick === undefined ? () => { } : OnClick;
return (
<a className={styles.menuItem} onClick={() => {
goToMenu && setActiveMenu(goToMenu);
setDirection(goBack ? 'menu-right' : 'menu-left');
handleClick();
}}>
<span className={styles.iconButton}>{leftIcon}</span>
{children}
<span className={styles.iconRight}>{rightIcon}</span>
</a>
);
}
type Props = {
menuName: string;
children: React.ReactNode | React.ReactNode[];
}
enum Direction {
LEFT = 'menu-left',
RIGHT = 'menu-right'
}
export default function DropdownSubMenu (props: Props) {
const [direction, setDirection] = useState<Direction>(Direction.LEFT);
const calcHeight = (element: HTMLElement) => {
if (element) setMenuHeight(element.offsetHeight);
};
return (
<CSSTransition in={activeMenu === props.menuName} unmountOnExit timeout={500} classNames={direction} onEnter={calcHeight}>
<div className={styles.menu}>
{props.children}
</div>
</CSSTransition>
);
}
type Props = {
children: React.ReactNode | React.ReactNode[];
}
export default function DropdownMenu (props: Props) {
const [activeMenu, setActiveMenu] = useState<string>('home');
const [menuHeight, setMenuHeight] = useState<number | null>(null);
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const child = dropdownRef.current?.firstChild as HTMLElement;
const height = getHeight(child);
if (height)
setMenuHeight(height);
}, []);
return (
<div className={styles.dropdown} style={{ height: `calc(${menuHeight}px + 2rem)` }} ref={dropdownRef}>
{props.children}
</div>
);
}
Conclusion :
More concretely I don't know what to put instead :
In DropdownSubMenu to set the menu height (setMenuHeight), and gets the active menu (activeMenu).
In DropdownItem, set the active menu, (setActiveMenu) and set the direction of the CSS animation (setDirection).
Source :
My code is adapted from these sources, But I want to make this code more professional, flexible and polymorphic :
https://github.com/fireship-io/229-multi-level-dropdown
I've been tried :
I tried to look at Redux, but I understood that it was only state global.
So it doesn't allow to define a different context for each component.
I tried to look at React 18, without success.
I have searched the StackOverflow posts, I have searched the state retrieval from the parents.
The use of components inside a component solves in effect the problem but we lose all the flexibility.
There are multiple ways to access a parent state from its children.
Pass the state as props
The preferred way is to pass the state and/or the change function to the children.
Example :
const App = () => {
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<div>
<button onClick={handleOpen}>Open modal</button>
<Modal onClose={handleClose} open={open} />
</div>
);
};
const Modal = ({ open, onClose }) => (
<div className={open ? "open" : "close"}>
<h1>Modal</h1>
<button onClick={onClose}>Close</button>
</div>
);
ReactDOM.render(<App />, document.querySelector("#app"));
Demo: https://jsfiddle.net/47s28ge5/1/
Use React Context
The first method becomes complicated when the children are deeply nested and you don't want to carry the state along the component tree.
You can then share a state across multiple children by using context.
const AppContext = React.createContext(undefined);
const App = () => {
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<AppContext.Provider value={{ open, onClose: handleClose }}>
<div>
<button onClick={handleOpen}>Open modal</button>
<Modal />
</div>
</AppContext.Provider>
);
};
const Modal = () => {
const { open, onClose } = React.useContext(AppContext);
return (
<div className={open ? "open" : "close"}>
<h1>Modal</h1>
<button onClick={onClose}>Close</button>
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#app"));
Demo: https://jsfiddle.net/dho0tmc2/3/
Using a reducer
If your code gets even more complicated, you might consider using a store to share a global state across your components.
You can take a look at popular options such as:
Mobx: https://mobx.js.org/ (my personal favorite)
Redux: https://redux.js.org/
RxJS: https://rxjs.dev/
I can say welcome to react in this moment and i glad for you
OK, i could understand what is your problem.
but there isn't problem and this bug cause from your low experience.
As i understand you want to click on a dropdown and open it.
and here we have nested dropdown.
I think it's your answer:
You should declare a state on each dropdown and don't declare state in parent.

Showing/hiding React components does not maintain internal state

I am trying to hide/show components in React based on some state. The main issue I am facing is to maintain the internal state of the components during hiding and showing. The below is the code that does what I expect and maintains the state of each of the components (Africa, Europe, America, Asia):
render() {
const {selectedTab} = this.state;
return (
<div>
<div className={selectedTab === 'africa' ? '' : 'hidden-tab'}><Africa /></div>
<div className={selectedTab === 'europe' ? '' : 'hidden-tab'}><Europe /></div>
<div className={selectedTab === 'america' ? '' : 'hidden-tab'}><America /></div>
<div className={selectedTab === 'asia' ? '' : 'hidden-tab'}><Asia /></div>
</div>
)
}
//regions.scss
.hidden-tab {
display: none
}
However, I am not satisfied with the cleanliness of the above code and would like to refactor, which is where I am facing issues. This is what I have done:
render() {
const {selectedTab} = this.state;
const tabToRegionMap = {
'africa': <Africa />,
'eruope': <Europe />,
'america': <America />,
'asia': <Asia />
};
const {[selectedTab]: selectedRegion, ...regionsToHide} = tabToRegionMap;
return (
<div>
<div className={'hidden-tab'}>
{Object.values(regionsToHide)}
</div>
{selectedRegion}
</div>
);
The above try does show/hide the copmonents but does not maintain the internal state of the components during hiding/showing - it seems like they are being unmounted and remounted every time.
Could anyone please help me solve the problem or suggest a better way of doing it? That would be much appreciated.
PS I would prefer not to move the state to the parent or Redux as there is a lot of boilerplate involved and the states of the individual components are very complex.
If I understand your question you are essentially looking for a way to clean up the code and keep the children components mounted.
The issue with the proposed solution is that each time it is rendered and supposed to hide tabs is it recreating the elements. They are swapping between being rendered into the <div className={'hidden-tab'}> and not, remounting each time when selected tab updates.
I suggest just abstracting the div elements into a new component that conditionally applies the 'hidden-tab' classname.
const Tab = ({ children, selectedTab, tab }) => (
<div className={selectedTab === tab ? '' : 'hidden-tab'}>
{children}
</div>
);
...
render() {
const {selectedTab} = this.state;
return (
<div>
<Tab selectedTab={selectedTab} tab='africa'><Africa /></Tab>
<Tab selectedTab={selectedTab} tab='europe'><Europe /></Tab>
<Tab selectedTab={selectedTab} tab='america'><America /></Tab>
<Tab selectedTab={selectedTab} tab='asia'><Asia /></Tab>
</div>
)
}
If you wanted to take it a step further, you could also abstract the wrapping div of these tabs into a container component that stored the selected tab and provided the value to children tabs via the Context API so a selectedTab wouldn't need to be explicitly passed to each.
Example:
import { createContext, useContext } from "react";
const TabContext = createContext({
selectedTab: null
});
const useSelectedTab = () => useContext(TabContext);
const Tabs = ({ children, selectedTab }) => (
<TabContext.Provider value={{ selectedTab }}>{children}</TabContext.Provider>
);
const Tab = ({ children, tab }) => {
const { selectedTab } = useSelectedTab();
return (
<div className={selectedTab === tab ? "" : "hidden-tab"}>{children}</div>
);
};
Usage:
render() {
const {selectedTab} = this.state;
return (
<Tabs selectedTab={selectedTab}>
<Tab tab='africa'><Africa /></Tab>
<Tab tab='europe'><Europe /></Tab>
<Tab tab='america'><America /></Tab>
<Tab tab='asia'><Asia /></Tab>
</div>
)
}

Deduct if the menu is open in react-select DropdownIndicator handler

I'd need to change the style of the arrow in react-select. I learned it can be done by using the components prop like in the code sample below.
However, the props coming to DropdownIndicator do not seem to provide any information if the menu is opened. I would need to use that information to change the arrow style depending on whether the menu is open or closed.
How could I get that information?
import ReactSelect, { components } from 'react-select';
...
const DropdownIndicator = (props) => {
const { isFocused } = props;
// Which prop tells if the menu is open? Certainly isFocused is not the correct one.
const caretClass = isFocused ? 'caret-up' : 'caret-down';
return (
<components.DropdownIndicator {...props}>
<div className={`${caretClass}`} />
</components.DropdownIndicator>
);
};
return (<ReactSelect
components={{ DropdownIndicator }}
placeholder={placeholder}
value={value}
onBlur={onBlur}
name={name}
...
/>)
I think react-select is passing all selectProps in custom components. And there is field called menuIsOpen in selectProps which is used to determine whether dropdown is open or not.
So you can access menuIsOpen by following:-
const DropdownIndicator = (props) => {
const { menuIsOpen } = props.selectProps;
// menuIsOpen will tell if dropdown is open or not
const caretClass = menuIsOpen ? 'caret-up' : 'caret-down';
return (
<components.DropdownIndicator {...props}>
<div className={`${caretClass}`} />
</components.DropdownIndicator>
);
};

Hypertexting by pasting a link using a toolbar

I have to say I started Javascript and React this week so I am not really familiar with it yet or with anything in the front end.
I have a link button in side a toolbar. I want to be able to click it, opening a text box where I can write a link, and then the text is hypertexted with it. Just want to say that any tip is appreciated.
Something like the following pictures.
I have coded the toolbar already and am using the slate-react module for the Editor (the text editor used). I am trying to follow what was done in a GitHub example, which is not exactly the same.
So, in essence, it is a link component inside a toolbar, which is inside a "Tooltip" component (that contains the horizontal toolbar plus another vertical bar), which is inside the editor.
My question is: How do I use react and slate editor to tie the Links together in the toolbar? Does the Link component need a state and onChange function? How can I include the Link component in the toolbar (button group), alongside the other buttons within "const Marks"?
I get that these questions might be basic but I am a beginner and would appreciate explanation.
My created Link component can wrap and unwrap link. When clicked,
onClickLink = event => {
event.preventDefault()
const { value } = this.state
const hasLinks = this.hasLinks()
const change = value.change()
if (hasLinks) {
change.call(this.unwrapLink)
}
else
{
const href = window.prompt('Enter the URL of the link:')
change.call(this.wrapLink, href)
}
this.onChange(change)
}
The wrap, unwrap and hasLinks boolean
class Links extends React.Component {
onChange = ({ value }) => {
this.setState({ value })
}
wrapLink(change, href) {
change.wrapInline({
type: 'link',
data: { href },
})
change.moveToEnd() }
unwrapLink(change) {
change.unwrapInline('link') }
hasLinks = () => {
const { value } = this.state
return value.inlines.some(inline => inline.type == 'link')
}
To render it in the editor.
const renderNode = ({ children, node, attributes }) => {
switch (node.type) {
case 'link': {
const { data } = node
const href = data.get('href')
return (
<a {...attributes} href={href}>
{children}
</a>
)
}
The "Tooltip" component, holding MarkSelect (the horizontal toolbar like the one in the picures) and another vertical bar called NodeSelector.
function Tooltip({ onChange, value }: Props) {
return (
<Fragment>
<SelectionPlacement
value={value}
render={({ placement: { left, top, isActive } }) => (
<div
id=...
{
isActive,
},
)}
style={{ left, top }}
>
<NodeSelector onChange={onChange} value={value} />
<MarkSelector onChange={onChange} value={value} />
</div>
)}
/>
The MarkSelector and other Marks (buttons) in the button group.
const MarkSelector = function MarkSelector({ onChange, value }: Props) {
return (
<ButtonGroup className=...>
{Marks.map(({ tooltip, text, type }) => {
const isActive = value.activeMarks.some(mark => mark.type === type);
return (
<Tooltip key={type} title={tooltip}>
<Button
className={classNames({ 'secondary-color': isActive })}
onMouseDown={event => {
event.preventDefault();
const change = value.change().toggleMark(type);
onChange(change);
}}
size=...
style=...
}}
>
{text}
</Button>
</Tooltip>
);
})}
</ButtonGroup>
);
};
const Marks = [
{
type: BOLD,
text: <strong>B</strong>,
tooltip: (
<strong>
Bold
<div className=...</div>
</strong>
),
},
{
type: ITALIC,
text:...
The editor with the tooltip.
render() {
const { onChangeHandler, onKeyDown, value, readOnly } = this.props;
return (
<div
className=...
id=...
style=..
>
{!readOnly && (
<EditorTooltip value={value} onChange={onChangeHandler} />
)}
<SlateEditor
ref=...
className=...
placeholder=...
value={value}
plugins={plugins}
onChange={onChangeHandler}
onKeyDown={onKeyDown}
renderNode={renderNode}
renderMark={renderMark}
readOnly={readOnly}
/>
{!readOnly && <ClickablePadding onClick={this.focusAtEnd} grow />}
</div>
);
}
Although it is not a recommended way to manipulate DOM directly in data-driven frontend frameworks, but you could always get the HTML element of the link, and set its innerHTML (which is the hypertext) according to internal states. This is hacky but it might works.

Categories