Scroll into a div that is hidden initially in react - javascript

Im building a web chat app in next.js and i have a emoji picker button that when its clicked the menu of emojis appear.The thing is that in order to the user sees the menu of the emojis he has to scroll down.I have tried scrollIntoView() but it doesnt seem to work,possibly im doing something wrong.
import {Picker} from "emoji-mart";
const pickerRef = useRef()
const[showEmojis,setShowEmojis]=useState(false);
useEffect(() => {
if(showEmojis) {
pickerRef.current.scrollIntoView(true)
}
} , [showEmojis])
return(
<EmoticonContainer >
{showEmojis && (<Picker ref={pickerRef} id="picker" style={{width: '100%'}} onSelect={addEmoji}/>)}
</EmoticonContainer>
)
const EmoticonContainer=styled.div`
display:flex;
flex-direction:column;
`;
I tried this code but it doesnt seem to work.It gives me this error: TypeError: pickerRef.current.scrollIntoView is not a function

Hey try adding a div below the picker and scroll to that div because sometimes
those third party libraries come with there css and you could not override them
import {Picker} from "emoji-mart";
const pickerRef = useRef()
const[showEmojis,setShowEmojis]=useState(false);
useEffect(() => {
pickerRef.current.scrollIntoView(true)
} , [showEmojis])
return(
<>
<EmoticonContainer >
<Picker id="picker" style={{width: '100%'}} onSelect={addEmoji}/>
</EmoticonContainer>
<div ref={emojiRef}></div>
</>
)
const EmoticonContainer=styled.div`
display:flex;
flex-direction:column;
`;
Try this and see it will scroll the way you need

Since it is hidden in the beginning then pickerRef.current should return undefined which would cause that error since undefined doesn't have the method .scrollIntoView. To fix this you could do ?.
pickerRef.current?.scrollIntoView()
see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
The optional chaining operator (?.) enables you to read the value of a property located deep within a chain of connected objects without having to check that each reference in the chain is valid.

Related

Conditionally render border colour React Native

I'm trying to conditionally change my border color based on whether the textInput is under focus or not.
<View
style={focus? { ...styles.inputRow, ...bgColorOnFocus }: { ...styles.inputRow, ...bgColorOnBlur }}>
This works as expected, but I'm having to repeat inputRow styles twice. I've been trying to improve the code with the following code where I add the object based on whether it's on focus or not and it's throwing an error.
<View style ={
{...styles.inputRow, focus? ...bgColorOnFocus: ...bgColorOnBlur}}
>
I'm not sure if I'm being silly, or if there's something missing that I'm not familiar with? Thanks for looking into this!
Typically a style prop accept an array as a value:
<View style={[styles.inputRow, isFocus ? bgColorOnFocus : bgColorOnBlur]}>
is a valid way to conditionally render some style.

React children count

i have a component like this
<Slider
Arrows
Autoplay
>
<SliderContentMall />
</Slider>
and in SliderContentMall i make a map like this
const SliderContentMall = (props) =>
{
return (
props.Data.map(DataValue =>
{
return (
<TouchableOpacity style={Styles.ImageContainer} onPress={() => {}>
</TouchableOpacity>
)
})
);
}
this map returns 3 object the things is in the slider component when i do
console.log(React.Children.count(this.props.children))
it only return 1 and i need the count of the 3, i cant make the map in the slider directly the map needs to be in the SliderContentMall
React.Children.count is doing what it's intended to do: there's a single child, and that child is a <SliderContentMall>. It's not going to check how many children are underneath that, and in fact it can't since those children havn't been rendered yet. Rendering goes from top down, so while you're rendering the <Slider>, you have not yet rendered the <SliderContentMall>, and thus the various <TouchableOpacity>'s havn't even been thought of yet.
If the slider needs to know how many pieces of data will be rendered, the most straightforward way to do that is as a prop to the slider. For example:
<Slider
Arrows
Autoplay
count={data.length}
>
<SliderContentMall data={data}/>
</Slider>
You are using map function over props.Data instead of props.children.

react-select dropdown opens inside modal

I have a custom modal where I have 2 react-select components inside. The modal body is ready to auto scroll in case the content exceeds its size, but the react-select components dropdown open inside the modal with this overflow, which is what i am not looking for. Without overflow, it works fine.
I'm working with CSS Modules.
<div className={styles.modalBody}>
{this.props.children}
</div>
.modalBody {
padding: padding;
flex: 1 1 auto;
height: 45vh;
max-height: 100%;
overflow: auto;
}
<Select
id={this.props.id}
className={styles[this.props.selectType ? this.props.selectType : 'selectWhite']}
classNamePrefix="select"
name={this.props.name}
value={selectedOption ? selectedOption : this.props.value}
placeholder={this.props.placeholder}
onChange={this.onChange}
options={this.props.options}
isDisabled={this.props.disabled}
isSearchable={false}/>
How can I fix this?
Thank you! :)
You want to look at the menuPortalTarget prop. There's a topic on this in the Advanced documentation, specifically with a modal example provided. Something like:
<Select
{...otherProps}
menuPortalTarget={document.body} />
You can use Menu Position as fixed by setting the prop,which in turn makes your dropdown as position fixed as
<Select menuPosition="fixed" />
A full example of how to display the select menu in a modal (I added comments at important lines and a step-by-step explanation below):
import React, { useRef } from 'react';
import Select from 'react-select';
import { Dialog } from '#mui/material';
const MyModal: React.FC = () => {
const ref = useRef<HTMLDivElement>(null);
return (
<Dialog
// ...
ref={ref} // stores a ref to the DOM node of the modal component
>
<Select
// ...
menuPortalTarget={ref.current} // pass the ref to `Select` to portal the select menu to the given DOM node
styles={{
menuPortal: defaultStyles => ({
...defaultStyles,
paddingBottom: '10px', // style the menu when it's portalled into the DOM node given to `menuPortalTarget`
}),
}}
/>
</Dialog>
);
};
export default MyModal;
Step-by-step explanation:
I create a ref with the React.useRef hook.
I'm getting a reference of the Dialog component (which in this example is a MUI dialog component (but could be any other Modal component) via ref={ref}
I pass that ref to react-select's Select component via menuPortalTarget={ref.current}.
I had to customize the placement of the menu via paddingBottom: '10px', because the menu would appear a bit too high in my case. You might have to adjust this differently.
Further comments:
document.body didn't do the trick for me. The menu appeared behind the modal for me in this case.
Unfortunately the react-select portaling docs do not contain any example code, so I hope this example is helpful to you.

How to add style - like margin - to react component?

So, expect two simple components that I have built:
import {Input} from 'semantic-ui-react';
import {Select} from 'semantic-ui-react';
const CategoriesDropdown = ({categories, onCategorySelected, selectedCategory}) => {
const handleChange = (e, {value})=>{
onCategorySelected(value);
};
return (
<Select placeholder="Select category" search options={categories} onChange={handleChange} value={selectedCategory} />
);
};
const IdentifiersInput = ({identifiers, onIdentifiersChanged}) => {
return (
<Input placeholder="Enter identifiers..." value={identifiers} onChange={onIdentifiersChanged}/>
);
};
Nothing fancy so far.
But now, I am building another component that displays those two in a flexbox row:
<Box>
<CategoriesDropdown categories={categories} selectedCategory={selectedCategoryId}
onCategorySelected={this.selectCategory}/>
<IdentifiersInput identifiers={identifiers} onIdentifiersChanged={this.changeIdentifiers}/>
</Box>
Unfortunately they are both displayed right next to each other without any margin in between.
Usually, I would just add a margin-left style to the second element, but because it is a React component, that doesn't work. Using style={{marginLeft: '20px'}} doesn't work as well, because the IdentifiersInput component doesn't use it.
I know that I can fix it by doing this: <Input style={style} ... inside the IdentifiersInput component.
However, this seems to be a very tedious way of achieving this goal. Basically, I have to add this to every single component I am writing.
I clearly must be missing something here. How am I supposed to apply such layout CSS properties to React components?
I think I understand.
1) Applying CSS directly to React Components does not work--I can confirm that.
2) Passing props down to the low level elements is tedious, confirmed but viable.
Notice hasMargin prop:
<Box>
<CategoriesDropdown
categories={categories}
selectedCategory={selectedCategoryId}
onCategorySelected={this.selectCategory}
/>
<IdentifiersInput
identifiers={identifiers}
onIdentifiersChanged={this.changeIdentifiers}
hasMargin
/>
</Box>
Possible input:
const IdentifiersInput = ({identifiers, onIdentifiersChanged, className, hasMargin }) => {
return (
<Input
className={className}
placeholder="Enter identifiers..."
value={identifiers}
onChange={onIdentifiersChanged}
style={hasMargin ? ({ marginLeft: '0.8rem' }) : ({})}
/>
);
};
NOTE: I do not like style as much as I like adding an additional class because classes can be adjusted via media queries:
const IdentifiersInput = ({identifiers, onIdentifiersChanged, className, hasMargin }) => {
const inputPosition = hasMargin ? `${className} margin-sm` : className
return (
<Input
className={inputPosition}
placeholder="Enter identifiers..."
value={identifiers}
onChange={onIdentifiersChanged}
/>
);
};
If you find inputPosition too verbose as shown above:
className={hasMargin ? `${className} margin-sm` : className}
3) You could accomplish it using a divider Component, sacreligious yet rapidly effective
<Box>
<CategoriesDropdown
categories={categories}
selectedCategory={selectedCategoryId}
onCategorySelected={this.selectCategory}
/>
<div className="divider" />
<IdentifiersInput
identifiers={identifiers}
onIdentifiersChanged={this.changeIdentifiers}
/>
</Box>
You can use media queries and control padding at any breakpoints if desired.
4) CSS pseudo-elements or pseudo-classes, I don't see any mention of them in answers so far.
MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
CSS Tricks: https://css-tricks.com/pseudo-class-selectors/
Usually, when you have a random collection of DOM elements, you can calculate a way using CSS to wrangle them into the correct position. The list of available pseudo-classes is in that MDN link. It honestly helps to just look at them and reason about potential combinations.
My current issue is I don't know what is in <Box /> other than it probably has a div with display: flex; on it. If all we have to go on is that and the div is called <div className="Box">, maybe some CSS like this will fix it:
.Box {
display: flex;
}
.Box:first-child {
margin-right: 0.8rem;
}
This is why it is extremely important to know exactly what the surrounding elements will or can be, and exactly which CSS classes/IDs are nearby. We are basically trying to hook into something and correctly identify the left child in Box and add margin to the right of it, or target the right child and add margin to the left of it (or depending on everything, target both and split the additional margin onto both).
Remember there is also ::before and ::after. You are welcome to get creative and find a solution that involves position:relative and position: absolute and adds no markup.
I will leave my answer like that for now, because I think either you already thought about pseudo-selectors, or you will quickly find something that works :)
That or the divider is actually quite viable. The fact you can use media queries alleviates you from concern of future management or scalability of the components. I would not say the same about <div style={{}} />.
As your component specializes another single component it would be a good practice to pass any props your wrapper does not care for to the wrapped component. Otherwise you will loose the ability to use the api of the original <Input>component including passing styles to it:
const IdentifiersInput = ({identifiers, onIdentifiersChanged, ...props}) = (
<Input
{...props}
placeholder="Enter identifiers..."
value={identifiers}
onChange={onIdentifiersChanged}
/>
);
There may be valid cases where you explicitly want to prevent users to be able to pass props to the wrapped component but that does not look like one of those to me.
I clearly must be missing something here. How am I supposed to apply
such layout CSS properties to React components?
You did not miss something. A react component has no generic way to be styled because it is no DOM element. It can have a very complicated and nested DOM representation or no representation at all. So at some point you as the designer of the component have to decided where the styles, ids and class names should be applied. In your case it is as easy as passing these props down and let the <Input> and <Select>component decide. I find that to be quite elegant rather than tedious.
I see several ways to do it, but the easiest I see would be to pass a className to IdentifiersInput like so:
<IdentifiersInput className="marginLeft" identifiers={identifiers} onIdentifiersChanged={this.changeIdentifiers}/>
Inside IdentifiersInput I would just set that class to the Input:
const IdentifiersInput = ({identifiers, onIdentifiersChanged, className}) => {
return (
<Input className={className} placeholder="Enter identifiers..." value={identifiers} onChange={onIdentifiersChanged}/>
);
};
Semantic UI's Input element can receive a className prop.
I would then just use CSS or SCSS to add styles to that particular class. In this case, the margin you want.

render simple HTML with react.cloneElement

React.cloneElement() always require first parameter as react component which should be passed as children in props.
Is there are way to pass a simple HTML node as a children. Please refer the code below for better understanding of my issue:
Dialog.jsx (Common component):
return (
<div className="app-dialog-jsx" ref={(ele) => this.ele = ele}>
{this.state.show && React.cloneElement(this.props.children, {
contentStyle: {
height: 400,
overflowY: 'auto',
overflowX: 'hidden'
},
method1: this. method1,
method2: this. method2
})}
</div>
);
now I can not pass:
<Dialog
ref={(dialog)=>this.dialog=dialog}
method1={()=>console.log(1)}
method2 ={()=>console.log(1)}
>
<h4>somethign</h4>
</Dialog>
H4 needs to be a react component otherwise it will not set the props in cloneElement. How can I send simple HTML here, any help?
Detail about why your fiddle is not working as expected.
See the code here:
{this.props.show && React.cloneElement(this.props.children, {
contentStyle: {
color:'red'
}
})}
Issue is in case of Custom Component like CCC, contentStyle will get passed as props and you are using it like this:
style={this.props.contentStyle}
That means at the end style will be applied on div not contentStyle. But in case of div, contentStyle will get applied and that will not change anything because div expect style not contentStyle.
To solve your problem rename contentStyle to style at all the places.
Check this working fiddle.
The best link that can describe the answer is here:
https://reactjs.org/warnings/unknown-prop.html
The unknown-prop warning will fire if you attempt to render a DOM element with a prop that is not recognized by React as a legal DOM attribute/property. You should ensure that your DOM elements do not have spurious props floating around.
To fix it we should split the props before rendering. like:
render(){
const {children, addCustomProps, ...props} = this.props;
return(<div {...props}>{children}</div>);
}
To avoid the warning, we should pass only those props to the DOM , which can be recognized as a HTML attribute or React attribute like className.

Categories

Resources