I have a multiple select. Instead of using the default behaviour I am showing the total selected items in the input when there are any values selected.
The problem is that I want also to be able to type for search when some items are selected. I have managed to do that by returning the last children item from CustomValueContainer . But it's still not enough.
What I would like to do is to show the total items when the input has no focus, and hide it when the input is focused. So I need to get the reference to the input value from my CustomValueContainer in order to see if it's focused or not. Any ideas?
This is my code https://stackblitz.com/edit/react-hv89pn:
import React, { Component } from 'react';
import { render } from 'react-dom';
import Select, { components as selectComponents, ValueContainerProps } from 'react-select';
import './style.css';
const CustomValueContainer = (props) => {
const length = props.getValue().length;
const children = props.children;
return length ? (
<selectComponents.ValueContainer {...props}>
<>
{length} selected
{children.slice(-1)}
</>
</selectComponents.ValueContainer>
) : (
<selectComponents.ValueContainer {...props}>{children}</selectComponents.ValueContainer>
);
};
class App extends Component {
constructor() {
super();
this.state = {
name: 'React'
};
}
render() {
return (
<Select
options = {[
{value: "1", label: "one"},
{value: "2", label: "two"},
{value: "3", label: "three"},
{value: "4", label: "four"},
]}
value={this.state.value}
onChange={value => this.setState({ value })}
isMulti
components={{ValueContainer: CustomValueContainer}}
clearable={true}
closeMenuOnSelect={false}
openMenuOnFocus={true}
/>
);
}
}
render(<App />, document.getElementById('root'));
Note that CustomValueContainer needs to be declared outside the render method (it does not work otherwise), but it's possible to pass a paremeter to it from render, I just need to pass it as a property of React Select and retrieve it from CustomValueContainer using props.selectProps.
Related
import { Dropdown, Button } from 'antd';
import { memo } from 'react';
import type { MenuProps } from 'antd';
const headerMeau: MenuProps['items'] = [
{
key: '1',
label: 'test'
},
{
key: '2',
label: 'test2'
},
{
key: '3',
label: 'test3'
}
];
const A = function () {
return <Button>111</Button>;
};
const BookSpace = memo(() => {
return (
<Dropdown menu={{ items: headerMeau }} placement="bottomLeft" trigger={['click']}>
<A></A>
{/* {A()} */}
</Dropdown>
);
});
BookSpace.displayName = 'BookSpace';
export default BookSpace;
It doesn't work when it is < A > < / A >
It normal when it is {A()}
expected:when the child is a React Component it's normal
enter image description here
It's because Dropdown component is the container for Button component and the actual Menu.
When you write a custom wrapper for Button, Dropdown doesn't know what to do with it. Because it is not a Button component anymore, it is a custom A component that wraps some JSX or Button components.
But when you do {A()} it returns the actual Button component of AntD and it works as intended.
I have a parent component which contains 4 buttons which I used to set a payment state. I want to pass the payment state to a a text field child component and also make it possible to use the text field to edit the state.
I have tried to use something like this how to update parent state in child component and pass to parent state back? which works but I am using formik with yup validation in parent component and it does not seem to detect the change of value from the text field child component.
Parent Component:
import React from "react";
import Child from 'path';
const animals = {
{
key:'1',
label:'Elephant',
isDefault:false
},
{
key:'2',
label:'Lion',
isDefault:false
},
{
key:'3',
label:'Bear',
isDefault:false
},
}
const Parent = () =>{
const defaultAnimalBtn = animals.some(choice => choice.isDefault === true);
const [ animalState, setAnimalState ] = useState({value: defaultAnimalBtn.label});
const handleAnimal = (index) => {
const selectedAnimal = animalState[index];
setAnimalState({value: selectedAnimal.label});
};
const handleAnimalChange = (event)=>{
event.preventDefault();
setAnimalState({value: event.target.value});
};
return(
<>
{
animals.map(({ key, label }, index)=>(
<Button
key={key}
variant={`${label === animalState.value ? 'contained':'outlined'}`}
onClick={() => handlePayment(index)}
>
{label}
</Button>
))
}
<Child
id="animal"
name="animal"
label="Animal"
amount={animalState.value}
onChange={handleAnimalChange}
/>
</>
);
}
Child Component:
import React from "react";
import { Field, useField } from 'formik';
const Child = ({ id, label, ...props })=>{
const [ field, meta ] = useField(props);
return(
<Field component={FormControl} fullWidth sx={{ mt: 3}} variant="standard">
<InputLabel htmlFor={id}>{label}</InputLabel>
<Input
id={id}
name={props.name}
onChange={props.onChange}
value={props.amount}
{ ...props }
startAdornment={<InputAdornment position="start">$</InputAdornment>}
label={label}
/>
{_renderHelperText()}
</Field >
);
}
export default Child;
Note: I am using React MUI as my component library. I am also rendering the Parent component inside a custom MultiStepForm using Formik and yup for form validation.
I followed this tutorial(Video:Formik Multi-Step Form with Material UI) to create the MultiStepForm.
EDIT: Link to Sandbox.
You shouldn't modify your parent state in your child, instead you should pass a function that modifies the data from parent to child and calling it as needed
I tend to use react functional components and hooks as I do not have a lot of experience with react. I want to use the react-dual-listbox class component within a parent functional component. Within this parent component I want to be able to access the selected state of the child class component. What is the best way to do this?
Child react-dual-listbox component from https://github.com/jakezatecky/react-dual-listbox
import React from 'react';
import DualListBox from 'react-dual-listbox';
const options = [
{ value: 1, label: 'Option One' },
{ value: 2, label: 'Option Two' },
];
class DualListChild extends React.Component {
state = {
selected: [1],
};
onChange = (selected) => {
this.setState({ selected });
};
render() {
const { selected } = this.state;
return (
<DualListBox
options={options}
selected={selected}
onChange={this.onChange}
/>
);
}
}
Contained within a standard functional component
function Parent() {
return(
<div>
<DualListChild/>
</div>
)
}
export default Parent;
Is it possible to, for example, have a hook in the parent component that changes state corresponding to what the dual listbox has selected? Essentially I want to pass state up but to a functional component? Is there a way to do this?
Thanks
You'd do something similar to what you do in DualListChild, except using the useState hook instead:
class DualListChild extends React.Component {
onChange = (selected) => {
this.props.onSelected(selected);
};
render() {
return (
<DualListBox
options={options}
selected={this.props.selected}
onChange={this.onChange}
/>
);
}
}
function Parent() {
const [selected, setSelected] = React.useState();
return (
<div>
<DualListChild selected={selected} onSelected={setSelected} />
</div>
)
}
Now you have access to selected (and even setSelected) inside your Parent component.
As an alternative way, you can have another state that keeps track of selected options in the parent and send its setter function to the child. Whenever the state of the child is changed, call the setter function that is coming from parent. With that way, the selected options state will be up-to-date value for the child and parent components any time.
function Parent() {
const [selectedOptions, setSelectedOptions] = useState([]);
return(
<div>
<DualListChild onSelectedOptionsChange={setSelectedOptions}/>
</div>
)
}
export default Parent;
class DualListChild extends React.Component {
...
onChange = (selected) => {
this.setState({ selected });
props.onSelectedOptionsChange(selected);
};
...
}
I have this code with a config file testType:
import React, { Component } from 'react';
import Select from "./components/Select/Select";
class App extends Component {
state = {
testType : [
{value : "test1", name : "Test1"},
{value : "test2", name : "Test2"},
]
}
render() {
return (
<>
<Select {...this.state.testType}/>
<Select {...this.state.testType}/>
</>
);
}
}
export default App;
Which I use to pass as props to a Select Component, below is the code for it Select.js :
import React from "react";
const selectoption = (props) => (
<select className="custom-select">
<option value={props.value}>{props.name}</option>
</select>
);
export default selectoption;
But it does not work, I don't see the props test1 and test2 in the select element.
The problem in your code is when you are doing <Select {...this.state.testType}/> the below is the data that will be received as prop in the Select component. So basically the array is getting converted to below format in the Select component.
{
"0": {
"value": "test1",
"name": "Test1"
},
"1": {
"value": "test2",
"name": "Test2"
}
}
So, instead of spreading it that way if it's being passed as list={[...this.state.testType]} then the passed array can be accessed in Select component as props.list or in the component itself it can be spread like const Select = ({ list }) => {...}. The passed list can be looped through using Array.map and then render all the options.
const { Component, Fragment } = React;
class App extends Component {
state = {
testType: [
{ value: "test1", name: "Test1" },
{ value: "test2", name: "Test2" }
]
};
render() {
return (
<Fragment>
<Select list={[...this.state.testType]} />
<Select list={[...this.state.testType]} />
</Fragment>
);
}
}
const Select = ({ list }) => (
<select className="custom-select">
{list.map(option => (
<option key={option.value} value={option.value}>{option.name}</option>
))}
</select>
);
ReactDOM.render(<App />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="react"></div>
For more info about rendering list elements in React please refer here.
I hope you are doing well.
I've just read your descriptions carefully.
Data you have passed through props is an array of objects.
So in the selectoption components the "prop" is an array of objects.
You cannot find "value" and "name" of this props.
You must map this array to get data of each options.
Or you can change the code like this.
<Select {...this.state.testType[0]}/>
<Select {...this.state.testType[1]}/>
Thank you for your vote.
I am a dedicated and reliable Full-Stack Developer with over 10 years of experience in Web Development.
I have strong skills and experience with React,Vue,Angular Frontend Development and Backend Development with RESTful APIs using Node.js and Laravel.
I can help you with my strong development skills.
You will be happy with my quality of code and fast delivery.
I am full time available and I can overlap at least 8 hrs a day in your timezone.
I have a shopping list app that is divided to two components as follows:
I implemented those two components as: ShoppingList and:ItemDetails
There is another component: ListItem that represents one item row (with edit and delete buttons).
ShoppinList maps over an array of ListItems.
My App component fetches an initial items array and sends it to ShoppingList.
Each time a click is made on the edit icon in a specific item row I set selectedItem object in my app component and render the ItemDetails component, passing it the selectedItem like so:
toggleDetailsPanel = (itemId) => (e) => {
this.setState((prevState, props) => {
return {
selectedItem: (prevState.selectedItem && prevState.selectedItem.id === itemId) ? null : this.findItemById(itemId),
};
});
};
And in the App render function I render it like that:
<div className={styles.details_outer_container}>
{this.state.selectedItem ? <ItemDetails handleSubmit={this.saveDetails} item={this.state.selectedItem}/> : null}
</div>
Whenever a click is made on the save button I run a function on the app component that updates the item in the items array (saveDetails).
Now I expected the ItemDetails component to render with new values each time I click on a different edit icon in a different item row, but the inputs values won't change, only the title is rendering.
I tried all solutions that I found, involving defaultValue, or setting value with getValue() function, or setting a dynamic key on the inputs, but nothing really helps.
This is my ItemDetails file:
import React from 'react';
import PropTypes from 'prop-types';
import { Grid, Row, Col, input, Button } from 'react-bootstrap';
import styles from './styles.css';
export default class ProductDetails extends React.Component {
static propTypes = {
handleSubmit: PropTypes.func.isRequired,
item: PropTypes.any.isRequired,
};
state = {
id: this.props.item.id,
name: this.props.item.name,
quantity: this.props.item.quantity,
price: this.props.item.price,
description: this.props.item.description,
};
// Set appropriate property in state by input name
handleInputChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
};
// Submit changed item to parent component
handleDetailsSubmit = (e) => {
this.props.handleSubmit(this.state);
e.preventDefault();
};
render() {
const item = this.props.item;
const itemName = item.name.toUpperCase() || '';
return (
<div className={styles.details_container}>
<div className="sub_header">
<span>{`${itemName} DETAILS`}</span>
</div>
<form className={styles.form_style}>
<p>
<label>{'Quantity'}</label>
<input type="text" ref="quantity" name="quantity" value={this.state.quantity} onChange={this.handleInputChange} />
</p>
<p>
<label>{'Price'}</label>
<input type="text" ref="price" name="price" value={this.state.price} onChange={this.handleInputChange}/>
</p>
<p>
<label>{'Description'}</label>
<textarea rows={2} ref="description" name="description" value={this.state.description} onChange={this.handleInputChange}/>
</p>
<div className={styles.button_div}>
<Button onClick={this.handleDetailsSubmit} bsStyle="primary" bsSize="small">
{'Save'}
</Button>
</div>
</form>
</`enter code here`div>
);
}
}
I understand this is React's way of handling forms but really don't know how to solve it.
I would really appreciate any help : )
The ProductDetails component only gets its initial values from item. From that point it is all maintained in state. So you need to reset the state when item changes.
Try adding something like this:
componentWillReceiveProps( newProps ) {
if ( this.props.item !== newProps.item ) {
this.setState( {
id: newProps.item.id,
name: newProps.item.name,
quantity: newProps.item.quantity,
price: newProps.item.price,
description: newProps.item.description,
} )
}
}