I'm trying to overwrite the default behaviour of 'noOptionsMessage' (default for noOptionsMessage: noOptionsMessage?: (obj: { inputValue: string }) => string | null;) of react-select using the components prop.
This is what it is stated in the code for react-select:
/*
This complex object includes all the compositional components that are used
in `react-select`. If you wish to overwrite a component, pass in an object
with the appropriate namespace.
*/
components?: SelectComponentsConfig<OptionType>;
This is what I currently have:
const components: Props['components'] = {
LoadingIndicator() {
return null;
},
DropdownIndicator() {
return null;
},
IndicatorSeparator() {
return null;
},
ClearIndicator(props) {
return (
<reactSelectComponents.ClearIndicator {...props}>
<XIcon size="small" />
</reactSelectComponents.ClearIndicator>
);
},
Input(props) {
return (
<reactSelectComponents.Input
{...props}
aria-describedby={(props as any).selectProps['aria-describedby']}
/>
);
},
LoadingMessage() {
return (
<Flex justify="center" marginY="xlarge">
<LoadingDots label="Loading options" />
</Flex>
);
},
ValueContainer(props) {
return (
<reactSelectComponents.ValueContainer {...props}>
<span
css={{
alignItems: 'center',
display: 'flex',
justifyContent: 'center',
width: 40,
}}
>
<SearchIcon color="dim" size="small" />
</span>
{props.children}
</reactSelectComponents.ValueContainer>
);
},
};
export function Autocomplete<Option extends BaseOption>(
props: AutocompleteProps<Option
) {
return (
<Select
components={components}
/>
);
}
I tried moving "const components: Props['components']" inside my Autocomplete component so I have access to the props. But this isn't working as expected. Any thoughts on how I can achieve this??
Related
I want to put the props from the child component into the value of useState from the parent component, but it also comes with undefined. How can I not get undefined?
Child component
const FilterSelect = props => {
const [filterUser, setFilterUser] = useState('all')
const handleFilterUser = ({ target }) => {
setFilterUser(target.value)
}
useEffect(() => {
props.propFunction(filterUser)
}, [filterUser])
return (
<Box sx={{ width: 120 }}>
<FormControl fullWidth>
<Select
size="small"
labelId="user-select-label"
id="user-select"
value={filterUser}
label="filter"
onChange={handleFilterUser}
>
</Select>
</FormControl>
</Box>
)
}
parent component
import FilterSelect from './components/FilterSelect'
const UserList = () => {
const [filterSelectOne, setFilterSelectOne] = useState('all')
const highFunction = text => {
setFilterSelectOne(text)
}
useEffect(() => {
highFunction()
}, [])
console.log(filterSelectOne) // 'all', undefined
return (
<Box sx={{ width: '100%' }}>
<Paper sx={{ width: '100%', mb: 2 }}>
<FilterSelect propFunction={highFunction} />
</Paper>
</Box>
)
}
Remove the use effect where you call the highFunction with no parameters in the parent component
I am making a Menu using React and Redux but am currently having an issue rendering the items when the user selects a category.
My goal is to render a list of items nested in the redux state of a particular category when it is clicked.
In Categories.js, it loads the categories from the redux store and displays them.
import React, { Component } from "react";
import { connect } from "react-redux";
import CategoryItems from "./CategoryItems"
import {
Card,
CardTitle,
CardImg,
Container,
Row,
Col,
CardBody,
Button
} from "shards-react";
class Categories extends Component {
handleClick = category => {
alert(category.title);
return (
<CategoryItems
category={category}
/>
);
};
render() {
let categoryList = this.props.categories.map(category => {
return (
<div key={category.id}>
<Col>
<Card
key={category.id}
onClick={() => {
this.handleClick(category);
}}
>
<CardBody>
<CardTitle>{category.title}</CardTitle>
</CardBody>
</Card>
</Col>
</div>
);
});
return (
<Container>
<Row>{categoryList}</Row>
</Container>
);
}
}
const mapStateToProps = state => {
return {
categories: state.categories,
};
};
export default connect(mapStateToProps)(Categories);
When a category is clicked, (the alert was just so I could make sure data made it) I have it set to render the items Array nested within the selected category.
import React, { Component } from 'react'
import {
Card,
CardTitle,
CardImg,
Container,
Row,
Col,
CardBody,
Button
} from "shards-react";
export class CategoryItems extends Component {
render() {
let items = this.props.category.items;
let categoryItems = items.map(item => {
return (
<Col className="">
<Card
className="mt-2 mb-2 item-col"
style={{ maxWidth: "500px" }}
key={item.id}
>
<CardBody>
<CardTitle style={{ position: "absolute", top: 20, right: 20 }}>
{item.title}
</CardTitle>
<CardImg
style={{ maxWidth: "200px" }}
src={item.img}
alt={item.title}
/>
<span
style={{ position: "absolute", bottom: 40, right: 100 }}
to="/"
// onClick={() => {
// this.handleClick(item.id);
// }}
>
<Button pill theme="info">
+
</Button>
</span>
<div style={{ position: "absolute", bottom: 40, right: 20 }}>
${item.price}
</div>
</CardBody>
</Card>
</Col>
);
});
return (
<Container className="menu-item-cont">
<Row>{categoryItems}</Row>
</Container>
);
}
}
export default CategoryItems
This part does not render the items and I don't get an error message.
I have also tried placing the items I would like rendered directly into the state just to see if I could get them to render on click similar to how the Categories.js is with no luck.
I am somewhat new to react so please forgive me if this is a beginner question.
Any help is appreciated as I have spent hours trying to figure this out.
The problem is that you're not rendering what gets returned from handleClick. What you need is a conditional render in the JSX, then determine whether or not to render the elements depending on a controlled flag variable in the state.
class Categories extends Component {
constructor() {
this.state = {
show: false,
category: undefined
}
this.toggle = (category) => {
this.setState({ show: !this.state.show, category })
}
}
render() {
let categoryList = this.props.categories.map(category => {
return (
<div key={category.id}>
<Col>
<Card
key={category.id}
onClick={e => this.toggle(category)}
>
<CardBody>
<CardTitle>{category.title}</CardTitle>
</CardBody>
</Card>
</Col>
</div>
);
});
return ([
<Container>
<Row>{categoryList}</Row>
</Container>,
(
this.state.show
? <CategoryItems category={this.state.category}/>
: null
)
]);
}
}
Basically I'm running a Reactjs application with Highcharts using the highcharts-react-official wrapper. I'm defining a highchart Component and the goal is to generate a report page with a dozen charts with different settings and data sources. I also want to add highstock stock tools components to it. The problem I'm finding is that when I click on Indicators button in a component (first option, top left) popop is triggered in all components.
Chart Component
// (...) Imports
class CustomGUIChart extends Component {
constructor(props) {
super(props);
const { data } = this.props;
this.state = {
options: {
legend: {
enabled: true
},
tooltip: {
enabled: false
},
series: data.map((set, index) => ({
...set,
id: `series-${index}`,
type: "line",
cursor: "pointer",
marker: {
enabled: false
}
})),
stockTools: {
gui: {
enabled: true
}
}
}
};
}
render() {
const { options } = this.state;
return (
<Card>
<CardContent style={{ padding: 0 }}>
<HighchartsReact
highcharts={Highcharts}
constructorType="stockChart"
containerProps={{ className: "chart" }}
options={options}
allowChartUpdate
/>
</CardContent>
</Card>
);
}
}
CustomGUIChart.propTypes = {
data: PropTypes.array.isRequired
};
export default CustomGUIChart;
Page Content calling chart component multiple times
// (...) Imports
const chartData = mockData.map((series) => ({
...series,
data: series.data.map((point) => {
const dateArr = point[0].split('-');
return [
Date.UTC(dateArr[0], dateArr[1], dateArr[2]),
point[1] * 100
];
})
}));
function SectionContent() {
return (
<>
<Grid item xs={12} sm={12}>
<Paper>
<CustomGUIChart
data={chartData}
/>
</Paper>
</Grid>
<Grid item xs={12} sm={12}>
<Paper>
<CustomGUIChart
data={chartData}
/>
</Paper>
</Grid>
<Grid item xs={12} sm={12}>
<Paper>
<CustomGUIChart
data={chartData}
/>
</Paper>
</Grid>
</>
);
}
export default Content;
How to handle this unexpected event trigger in different components/charts?
Here is a working Demo:
You need to declare unique bindingsClassName per each chart.
navigation: {
bindingsClassName: 'chart2'
}
Live demo: https://jsfiddle.net/BlackLabel/y5wxvukr/1/
Related thread: https://github.com/highcharts/highcharts/issues/10599
AP Reference: https://api.highcharts.com/highstock/navigation.bindingsClassName
I'm currently creating a card wrapper for a custom bootstrap card. I'm trying to make it so the developer can dynamically render elements on the front and back of the card depending on needs. This is what I have so far
import React, { useState, ReactElement } from 'react';
import { Card, ListGroup } from 'react-bootstrap';
interface Side {
imgSrc?: string;
title?: string;
text?: string;
body?: (HTMLElement | JSX.Element)[];
}
interface CCardProps {
className?: string;
front?: Side;
back?: Side
}
const CCard: React.FC<CCardProps> = ({ className, front = {}, back = front }) => {
const [cardValue, setCardValue] = useState(front);
return (
<Card className={className || ''} onClick={() => cardValue === front ? setCardValue(back) : setCardValue(front)} style={{ width: "19rem", minHeight: '26rem' }}>
{cardValue.imgSrc && <Card.Img style={{ maxHeight: '100%', maxWidth: '100%' }} variant="top" src={cardValue.imgSrc} />}
<Card.Body>
{cardValue.title && <Card.Title>{cardValue.title}</Card.Title>}
{cardValue.text && <Card.Text>{cardValue.text}</Card.Text>}
{cardValue.body && cardValue.body.length > 0 && cardValue.body.map((item, index) =>
// What do I do here?
}
</Card.Body>
</Card>
)
};
export default CCard;
// Example use
<CCard
front={{
imageUrl: 'cutePuppyPhoto',
body: [
Click me!,
]
}}
back={{
title: 'This is a title',
text: 'This is some text',
body: [
<Card.Text>Hello There</Card.Text>
]
}}
/>
cardValue.body can be both HTML Elements <a> or <h1> etc but also other JSX elements (if I want to throw in a <Card.Text> or something. I don't now how to dynamically map both of these with a key. Any help will be appreciated.
Turns out I'm dumb, the array is already filled with elements and not values so I don't need a map.
<Card className={className} onClick={() => cardValue === front ? setCardValue(back) : setCardValue(front)} style={{ width: "19rem", minHeight: '26rem' }}>
{cardValue.imgSrc && <Card.Img style={{ maxHeight: '100%', maxWidth: '100%' }} variant="top" src={cardValue.imgSrc} />}
<Card.Body>
{cardValue.title && <Card.Title>{cardValue.title}</Card.Title>}
{cardValue.text && <Card.Text>{cardValue.text}</Card.Text>}
{cardValue.body}
</Card.Body>
</Card>
Map values, not elements kids
I have small portion of my app rendering a dropdown selection so users can view some brief information about library versions within the app.
The first dropdown is for the user to pick what they want view, for brevity I just added the relevant option for this question.
The second dropdown renders a fetched list of numbers pertaining to deviceIds. These deviceIds are fetched from a child class and then passed via props up to the parent class.
Now, In Semantic-UI-React, they have a dropdown module with a clearable prop which I am using on this dropdown populated with deviceIds. Below in VersionNumber class, you can see the props defined for the aforementioned dropdown.
The clearable prop lets the user remove the selected input from the dropdown. However, in my app when the input is removed it calls the onChange function which I do not want. I tried passing a custom change function to the clearable prop where I'm attempting to reset the state variable deviceId back to a undefined value if it's "cleared" by the user.
Is this possible? I'm pretty sure I'm taking the correct approach, but may be having an issue where the onChange function is firing before the clearable passed function.
I have a codesandbox that reproduces this problem here.
import React, { Component } from "react";
import ReactDOM from "react-dom";
import axios from "axios";
import {
Dropdown,
Form,
Divider,
Input,
Message,
Loader
} from "semantic-ui-react";
class App extends Component {
constructor(props) {
super(props);
this.state = {
viewSelection: "",
deviceId: "",
versionNum: "",
isLoading: true
};
}
handleViewSelection = (e, { value }) => {
this.setState({ viewSelection: value }, () => {
console.log("view election --> ", this.state.viewSelection);
});
};
onDeviceIdChange = async (e, { value }) => {
console.log("device id value -->", value);
this.setState({ deviceId: value }, () => {
console.log(
"deviceId value updated to state on select -->",
this.state.deviceId
);
});
try {
const { data } = await axios.get(`/deviceId/${value}`);
const version = data.data.versionNumber;
this.setState({ versionNum: version.version, isLoading: false }, () => {
console.log("callback from admin version fetch", this.state.versionNum);
});
} catch (error) {
console.error(Error(`Error getting the selected deviceId ${error.message}`));
}
};
handleClear = () => {
this.setState({ deviceId: "" }, () => {
console.log("handleClear function fired");
});
};
render() {
const { viewSelection, deviceId, versionNum, isLoading } = this.state;
return (
<div
style={{ display: "flex", minHeight: "100vh", flexDirection: "column" }}
>
<div style={{ flex: 1 }}>
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
flex: "1"
}}
>
<div style={{ width: "1000px" }}>
<Divider style={{ marginTop: "7em" }} horizontal>
View Data
</Divider>
<Message style={{ marginTop: "2em" }} info>
<Message.Header>
Want to see more specific information? Log in
here.
</Message.Header>
</Message>
<Form.Field style={{ marginTop: "2em" }}>
<label>Select data to view</label>
<Dropdown
scrolling
placeholder="Pick an option"
value={viewSelection}
fluid
selection
multiple={false}
search
options={viewOptions}
onChange={this.handleViewSelection}
/>
</Form.Field>
{this.state.viewSelection && (
<div style={{ marginTop: "2em" }}>
{viewSelection && viewSelection === "versionNumber" ? (
<>
<VersionNumber
onDeviceIdChange={this.onDeviceIdChange}
deviceId={deviceId}
handleClear={this.handleClear}
/>
<div>
<label style={{ display: "block", marginTop: "2em" }}>
Version Number
</label>
{isLoading ? (
<Loader active inline="centered" />
) : (
<Input value={versionNum} fluid readOnly />
)}
</div>
</>
) : null}
</div>
)}
</div>
</div>
</div>
</div>
);
}
}
class VersionNumber extends Component {
constructor(props) {
super(props);
this.state = {
deviceId: "",
deviceIds: []
};
}
async componentDidMount() {
await this.getDeviceIds();
}
getDeviceIds = async () => {
try {
const { data } = await axios.get("/deviceIds");
console.log("device IDs --> ", data);
const deviceIds = data.data.deviceIds;
this.setState(
{
deviceIds: deviceIds
},
() => {
console.log(
"setState callback with updatesd deviceIds state -->",
this.state.deviceIds
);
}
);
} catch (error) {
console.error(Error(`Error getting deviceIds: ${error.message}`));
}
};
render() {
const { onDeviceIdChange, deviceId, handleClear } = this.props;
const { deviceIds } = this.state;
return (
<Form.Field required>
<label style={{ display: "block" }}>Device Id</label>
<Dropdown
id="deviceId"
value={deviceId}
onChange={onDeviceIdChange}
selection
fluid
placeholder="Please select an device id"
clearable={handleClear}
options={deviceIds.map(id => {
return {
text: id.id,
value: id.id,
key: id.id
};
})}
style={{ marginTop: ".33", marginBottom: "2em" }}
/>
</Form.Field>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
clearable prop expects a boolean value. You can change your onChange callback to consider the scenario where the value is going to be passed in as null / undefined
The function can be modified as below to reset the deviceId and prevent the API call
onDeviceIdChange = async (e, { value }) => {
// VALUE WILL BE PASSED IN AS "" WHEN THE CLEAR OPTION IS CLICKED
console.log("device id value -->", value);
this.setState({ deviceId: value }, () => {
console.log(
"deviceId value updated to state on select -->",
this.state.deviceId
);
});
// THIS IS THE CONDITION TO CATCH THE CLEARABLE INVOCATION
if(!value) {
this.handleClear();
return;
}
...
};
if(!value) return; Think of this condition as the execution that the clearable option will invoke. You can invoke anything before the return to ensure that your application handles the clear option in the way you expect it to.