access to inner input element in react-select - javascript

there is an script out there called farsitype.js and i want to use it with react-select component but there is no way to direct access to inner input field to add attribute to it. is there any way that we can add attributes to inner input field in react-select?

If you are talking about this react-select library. You can customize the Inputcomponent like so:
import React from 'react';
import Select, { components } from 'react-select';
import { typeOptions } from '../data';
const Input = props => {
// add attribues to the component below
return <components.Input {...props} />;
};
export default () => (
<Select
components={{ Input }}
options={typeOptions}
/>
);

Related

how to create and export react components like ComponentA.ComponentB [duplicate]

This question already has answers here:
Using dot notation with functional component
(2 answers)
Closed 1 year ago.
this really sound like a simple thing to know but I couldn't find any articles/resources so asking here.
how can I create components like Form.Input, Form.Select and use as follows -
import Form from '../Form';
...
const MyComponent = (props) => {
...
return (
<Form>
<Form.Input />
<Form.Select />
</Form>
);
}
So I basically want to understand what Form component is like that it is behaving like an object with sub exports like Input as Form.Input and Form.Select.
I've seen such codes at some places. Also we use React.Component, don't we?
I only know to export default and named components so I can create different exports like Input and Select but can't figure out how exporting Form.Input could work ?
The dot notation may make this seem like a subcomponent, but it is just another component attached to the Form component. Take a look at the codesandbox example
Basically, you have a component Form:
export const Form = (props) => {
const Item = ({ children }) => <p> {children} </p>;
Form.Item = Item;
return <div>{props.children}</div>;
};
And you can access this like:
import "./styles.css";
import { Form } from "./Form";
export default function App() {
return (
<Form>
<Form.Item> Test </Form.Item>
<Form.Item> Test 2 </Form.Item>
</Form>
);
}
You can add them to your Form component before exporting :
import React from 'react';
const Form = ({ Input, Select, children }) => (
...
);
const Input = ({ children }) => children;
Form.Input = Input;
const Select = ({ children }) => children;
Card.Select = Select;
export default Form;
More info here if you want :
https://dev.to/shayanypn/buckle-with-react-sub-component-10ll
On file Form.js
export default {
Input,
Select,
}
const Input = (props) => {
// xyz
}
const Select = (props) => {
// xyz
}

MaterialUI Redux connect() using TextArea and submitting form. Warning about refs in func components [duplicate]

I have the following (using Material UI)....
import React from "react";
import { NavLink } from "react-router-dom";
import Tabs from "#material-ui/core/Tabs";
import Tab from "#material-ui/core/Tab";
function LinkTab(link){
return <Tab component={NavLink}
to={link.link}
label={link.label}
value={link.link}
key={link.link}
/>;
}
In the new versions this causes the following warning...
Warning: Function components cannot be given refs. Attempts to access
this ref will fail. Did you mean to use React.forwardRef()?
Check the render method of ForwardRef.
in NavLink (created by ForwardRef)
I tried changing to...
function LinkTab(link){
// See https://material-ui.com/guides/composition/#caveat-with-refs
const MyLink = React.forwardRef((props, ref) => <NavLink {...props} ref={ref} />);
return <Tab component={MyLink}
to={link.link}
label={link.label}
value={link.link}
key={link.link}
/>;
}
But I still get the warning. How do I resolve this issue?
Just give it as innerRef,
// Client.js
<Input innerRef={inputRef} />
Use it as ref.
// Input.js
const Input = ({ innerRef }) => {
return (
<div>
<input ref={innerRef} />
</div>
)
}
NavLink from react-router is a function component that is a specialized version of Link which exposes a innerRef prop for that purpose.
// required for react-router-dom < 6.0.0
// see https://github.com/ReactTraining/react-router/issues/6056#issuecomment-435524678
const MyLink = React.forwardRef((props, ref) => <NavLink innerRef={ref} {...props} />);
You could've also searched our docs for react-router which leads you to https://mui.com/getting-started/faq/#how-do-i-use-react-router which links to https://mui.com/components/buttons/#third-party-routing-library. The last link provides a working example and also explains how this will likely change in react-router v6
You can use refs instead of ref. This only works as it avoids the special prop name ref.
<InputText
label="Phone Number"
name="phoneNumber"
refs={register({ required: true })}
error={errors.phoneNumber ? true : false}
icon={MailIcon}
/>
In our case, we were was passing an SVG component (Site's Logo) directly to NextJS's Link Component which was a bit customized and we were getting such error.
Header component where SVG was used and was "causing" the issue.
import Logo from '_public/logos/logo.svg'
import Link from '_components/link/Link'
const Header = () => (
<div className={s.headerLogo}>
<Link href={'/'}>
<Logo />
</Link>
</div>
)
Error Message on Console
Function components cannot be given refs. Attempts to access this ref will fail.
Did you mean to use React.forwardRef()?
Customized Link Component
import NextLink from 'next/link'
import { forwardRef } from 'react'
const Link = ({ href, shallow, replace, children, passHref, className }, ref) => {
return href ? (
<NextLink
href={href}
passHref={passHref}
scroll={false}
shallow={shallow}
replace={replace}
prefetch={false}
className={className}
>
{children}
</NextLink>
) : (
<div className={className}>{children}</div>
)
}
export default forwardRef(Link)
Now we made sure we were using forwardRef in the our customized Link Component but we still got that error.
In order to solve it, I changed the wrapper positioning of SVG element to this and :poof:
const Header = () => (
<Link href={'/'}>
<div className={s.headerLogo}>
<Logo />
</div>
</Link>
)
If you find that you cannot add a custom ref prop or forwardRef to a component, I have a trick to still get a ref object for your functional component.
Suppose you want to add ref to a custom functional component like:
const ref = useRef();
//throws error as Button is a functional component without ref prop
return <Button ref={ref}>Hi</Button>;
You can wrap it in a generic html element and set ref on that.
const ref = useRef();
// This ref works. To get button html element inside div, you can do
const buttonRef = ref.current && ref.current.children[0];
return (
<div ref={ref}>
<Button>Hi</Button>
</div>
);
Of course manage state accordingly and where you want to use the buttonRef object.
to fix this warning you should wrap your custom component with the forwardRef function as mentioned in this blog very nicely
const AppTextField =(props) {return(/*your component*/)}
change the above code to
const AppTextField = forwardRef((props,ref) {return(/*your component*/)}
const renderItem = ({ item, index }) => {
return (
<>
<Item
key={item.Id}
item={item}
index={index}
/>
</>
);
};
Use Fragment to solve React.forwardRef()? warning
If you're using functional components, then React.forwardRef is a really nice feature to know how to use for scenarios like this. If whoever ends up reading this is the more hands on type, I threw together a codesandbox for you to play around with. Sometimes it doesn't load the Styled-Components initially, so you may need to refresh the inline browser when the sandbox loads.
https://codesandbox.io/s/react-forwardref-example-15ql9t?file=/src/App.tsx
// MyAwesomeInput.tsx
import React from "react";
import { TextInput, TextInputProps } from "react-native";
import styled from "styled-components/native";
const Wrapper = styled.View`
width: 100%;
padding-bottom: 10px;
`;
const InputStyled = styled.TextInput`
width: 100%;
height: 50px;
border: 1px solid grey;
text-indent: 5px;
`;
// Created an interface to extend the TextInputProps, allowing access to all of its properties
// from the object that is created from Styled-Components.
//
// I also define the type that the forwarded ref will be.
interface AwesomeInputProps extends TextInputProps {
someProp?: boolean;
ref?: React.Ref<TextInput>;
}
// Created the functional component with the prop type created above.
//
// Notice the end of the line, where you wrap everything in the React.forwardRef().
// This makes it take one more parameter, called ref. I showed what it looks like
// if you are a fan of destructuring.
const MyAwesomeInput: React.FC<AwesomeInputProps> = React.forwardRef( // <-- This wraps the entire component, starting here.
({ someProp, ...props }, ref) => {
return (
<Wrapper>
<InputStyled {...props} ref={ref} />
</Wrapper>
);
}); // <-- And ending down here.
export default MyAwesomeInput;
Then on the calling screen, you'll create your ref variable and pass it into the ref field on the component.
// App.tsx
import React from "react";
import { StyleSheet, Text, TextInput, View } from "react-native";
import MyAwesomeInput from "./Components/MyAwesomeInput";
const App: React.FC = () => {
// Set some state fields for the inputs.
const [field1, setField1] = React.useState("");
const [field2, setField2] = React.useState("");
// Created the ref variable that we'll use down below.
const field2Ref = React.useRef<TextInput>(null);
return (
<View style={styles.app}>
<Text>React.forwardRef Example</Text>
<View>
<MyAwesomeInput
value={field1}
onChangeText={setField1}
placeholder="field 1"
// When you're done typing in this field, and you hit enter or click next on a phone,
// this makes it focus the Ref field.
onSubmitEditing={() => {
field2Ref.current.focus();
}}
/>
<MyAwesomeInput
// Pass the ref variable that's created above to the MyAwesomeInput field of choice.
// Everything should work if you have it setup right.
ref={field2Ref}
value={field2}
onChangeText={setField2}
placeholder="field 2"
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
app: {
flex: 1,
justifyContent: "center",
alignItems: "center"
}
});
export default App;
It's that simple! No matter where you place the MyAwesomeInput component, you'll be able to use a ref.
I just paste here skychavda solution, as it provide a ref to a child : so you can call child method or child ref from parent directly, without any warn.
source: https://github.com/reactjs/reactjs.org/issues/2120
/* Child.jsx */
import React from 'react'
class Child extends React.Component {
componentDidMount() {
const { childRef } = this.props;
childRef(this);
}
componentWillUnmount() {
const { childRef } = this.props;
childRef(undefined);
}
alertMessage() {
window.alert('called from parent component');
}
render() {
return <h1>Hello World!</h1>
}
}
export default Child;
/* Parent.jsx */
import React from 'react';
import Child from './Child';
class Parent extends React.Component {
onClick = () => {
this.child.alertMessage(); // do stuff
}
render() {
return (
<div>
<Child childRef={ref => (this.child = ref)} />
<button onClick={this.onClick}>Child.alertMessage()</button>
</div>
);
}
}

How to pass props to custom components in react-select

I am trying to use a custom component as an input field in react-select. Since I need validation I am trying to use HTML5 oninvalid (onInvalid in JSX) for my input tag and set the custom message for oninvalid. However I am unable to pass the message as a prop to the component that I am setting in select. Below is my code.
Input.js
import React from "react";
import { components } from "react-select";
export default class Input extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log("component mounted");
}
setInvalidMessage = event => {
event.target.setCustomValidity("Custom Message");
};
render() {
if (this.props.isHidden) {
return <components.Input {...this.props} />;
}
return (
<components.Input
{...this.props}
required
onInvalid={this.setInvalidMessage}
/>
);
}
}
example.js
import React from "react";
import Input from "./Input";
import Select from "react-select";
import { colourOptions } from "./docs/data";
const InputBoxWithText = props => {
return <Input {...props} />;
};
export default () => (
<form>
<Select
closeMenuOnSelect={true}
components={{ InputBoxWithText }}
options={colourOptions}
/>
<input type="submit" />
</form>
);
If I pass Input in components attribute I get the hard coded message in Input.js. If I pass InputBoxWithText I don't see Input mounting at all.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Example from './example';
ReactDOM.render(
<Example />,
document.getElementById('root')
);
Here is CodeSandBox.io URL.
Can any one let me know if what am I doing wrong.
It's better to pass custom props via select:
props.selectProps
To avoid re-creating of Custom component each time Select updates, what may cause unexpected bugs.
In my case I was passing errors in such way:
<Select
defaultValue={values}
selectProps={{ errors }}
isMulti
options={inventoryList}
onChange={changeTreeElement}
// #ts-ignore
styles={colourStyles}
/>
Then access it like selectProps.selectProps.errors in colourStyles methods.
I managed to pass my custom props using an arrow function
See docs for defining components
const Input = (inputProps: InputProps) => (
<components.Input {...inputProps} />
);
<Select
closeMenuOnSelect={true}
options={colourOptions}
components={{Input}}
/>
I don't have the solution (i'm looking for the same thing as well), but you example has multiple errors.
To load the input you have to write components={{ Input: InputBoxWithText }}, since the component name for Input is not InputBoxWithText.
Also the onInvalid does not seem to be part of the Input API, so it will never trigger. Are you trying to use the <input oninvalid="" />..?
In version 5 the only way to use custom props with typescript is to use module augmentation.
So in my project I opened react-app-env.d.ts and added there this:
import { GroupBase } from 'react-select'
declare module 'react-select/dist/declarations/src/Select' {
export interface Props<Option, IsMulti extends boolean, Group extends GroupBase<Option>> {
customOnClear: () => void;
}
}
You pass the prop to the select like this:
import Select from "react-select";
<Select customOnClear={() => {/* Your custom clear */} />
And use it in your custom component like this:
const ClearIndicator = ({ selectProps }: ClearIndicatorProps<Option, false>) => {
const { customOnClear } = selectProps
return <InputClear onClick={customOnClear} />
}
Docs:
https://react-select.com/typescript#custom-select-props

States do not change when using styled components in React

I am trying to implement a search bar while using the styled-components library for styling. My issue is that the queried value never changes if I used styled-components. This is my code
import styled from 'styled-components'
import React, from 'react'
const SearchBar = styled.input`
margin-top: 35px;
float: right;
`
class Header extends React.Component {
state = {
query: '',
}
handleNewQuery = () => {
this.setState({
query: this.search.value,
})
console.log(this.search.value);
}
render () {
return (
<SearchBar
placeholder='Search for...'
ref={input => this.search = input}
onChange={this.handleNewQuery}
/>
)
}
}
Which only works if I swap SearchBar with input, otherwise the log prints undefined
The base issue is the the ref that is being created is returning a StyledComponent, not an HTML input element. It simply does not have a value property. The reason it starts working when you removing the styled aspect and simply render an <input />, is then the ref is an actual HTML input element with a value property. Try logging the ref in the change event to see this with first the styled component then a standard input. Either way I'd try approaching it as a Controlled Component using value property and event.target.value instead of attempting to extract the value from a ref.
import React, { Component } from 'react';
import styled from 'styled-components';
import './style.css';
const SearchBar = styled.input`
margin-top: 35px;
float: right;
`;
class Header extends Component {
constructor() {
super();
this.state = {
query: ''
};
}
handleNewQuery = (e) => {
this.setState({
query: e.target.value
})
}
render() {
return (
<div>
<SearchBar
placeholder='Search for...'
onChange={this.handleNewQuery}
value={this.state.query}
/>
</div>
);
}
}
If you absolutely must use a ref with this styled component. You can used the property innerRef which is specific to styled components to access the underlying HTML input element. This would technically give you access the value property. Once again though, the best approach would simply be using a controlled component as described above. The below example is using the newer approach to creating refs, but it would depend on your version of React being used.
<SearchBar
placeholder='Search for...'
onChange={this.handleNewQuery}
value={this.state.query}
innerRef={this.search}
/>
Here is a StackBlitz showing the functionality in action including the innerRef.
Hopefully that helps!
SearchBar should take a value prop instead of using a ref to get the value. Something like this:
<SearchBar value={this.state.search} ... />

Dynamically creating react components that don't refresh the DOM on every change

I'm working on a React app where we are rendering an unknown number of form elements passed in from another application. I have code setup to pull in all possible components and dynamically render each with props when it's needed (below), but every time I make any change (i.e. entering text in a text field, clicking a checkbox, etc.), every form element is updated. I'm unsure how to get around this because the rendering is done inside a map function, and I can't get rid of the map function because we have no idea what or how many elements will be sent in to be rendered. I am using redux, so can't adequately use shouldComponentUpdate, but I have used the {pure: true} option in connect, with no luck. Any ideas on how to do this?
import React from 'react';
import {
InputBox,
RadioGroup,
ComboBox,
CheckboxGroup,
DatePicker
} from './components';
import {connect} from "react-redux";
import * as actions from 'redux/actions/actions';
import {convertToVariable} from 'components/Utility/utils'
const componentLookup = {
"InputBox": InputBox,
"RadioGroup": RadioGroup,
"ComboBox": ComboBox,
"CheckboxGroup": CheckboxGroup,
"DateField": DatePicker
};
class FormPanel extends React.Component {
constructor(props) {
super(props);
this.logData = this.logData.bind(this);
}
logData(eventValues) {
this.props.updateDialogValues(eventValues);
}
render() {
return (
<div>
{this.props.uiContent.map((element) => {
const Tag = componentLookup[element.type];
return (
<Tag
{...element.defaults}
onValueChange={this.logData}
key={element.defaults.label}
name={element.defaults.variable || convertToVariable(element.defaults.label)}
/>
)
})}
</div>
);
}
}
function mapStateToProps(state) {
return {
dialogValues: state.app.dialogValues,
actionInfo: state.xyz.actionInfo,
actionPayload: state.xyz.actionPayload
}
}
function mapDispatchToProps(dispatch) {
return {
updateDialogValues: (dialogValues) => {
dispatch(actions.updateDialogValues(dialogValues))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps, undefined, {pure: true})(FormPanel)

Categories