I am trying to use reac-window with Autocomplete.
This is the code base:
function renderRow(props) {
const { data, index, style } = props;
return React.cloneElement(data[index], {
style: {
...style,
top: style.top,
},
});
}
const ListboxComponent = React.forwardRef(function ListboxComponent(props, ref) {
const { children, role, ...other } = props;
const itemData = React.Children.toArray(children);
return (
<div ref={ref} {...other}>
<FixedSizeList
width="100%"
height={150}
itemSize={50}
itemCount={itemData.length}
itemData={itemData}
role={role}>
{renderRow}
</FixedSizeList>
</div>
);
});
export default function VirtualizedAutocomplete() {
const accountNumberData = useSelector((state) => state.commAccountNumber.data);
const isLoadingAccountNumber = useSelector((state) => state.commAccountNumber.isLoading);
return (
<Autocomplete
id="virtualize-demo1"
multiple
disableListWrap
ListboxComponent={ListboxComponent}
options={accountNumberData}
renderInput={params => (
<TextField
{...params}
variant="outlined"
label="virtualize-demo1"
fullWidth
/>
)}
/>
);
According the documentation I need to make sure that the container has the role attribute set to listbox.
I assume that this is the issue but I am not sure what is wrong with my code?
Thank you
Related
I use a map with custom markers on the site. On the right is a list of houses that match the markers. I need to implement the following: when clicking on a marker, the house that corresponds to the marker goes to 1st place in the list. Marker coordinates and house information comes from Firebase. Now I have implemented the code for this logic, but when I click on the marker, I get an error - Objects are not valid as a React child. How can it be solved?
const List = ({ selectedHouse }) => {
const [houseTitles, setHouseTitle] = useState([]);
useEffect(() => {
const q = query(collection(db, "map-markers"));
onSnapshot(q, (querySnapshot) => {
setHouseTitle(
querySnapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
);
});
}, []);
return (
<div style={{ width: "50%" }}>
{<ListItem title={houseTitles[selectedHouse]} />}
{houseTitles
.filter((title, index) => index !== selectedHouse)
.map((title, index) => (
<ListItem key={index} title={title.data.title} />
))}
</div>
);
};
console.log(houseTitles[selectedHouse]):
You don't need curly brackets when using components like that;
...
...
return (
<div style={{ width: "50%" }}>
{houseTitles[selectedHouse] && houseTitles[selectedHouse].data ? <ListItem title={houseTitles[selectedHouse].data.title} />:null }
{houseTitles
.filter((title, index) => index !== selectedHouse)
.map((title, index) => (
<ListItem key={index} title={title.data.title} />
))}
</div>
);
...
...
Also, you probably try to print title in ListItem component yet you pass the whole object for the selectedHouse
Additional info; selectedHouse is empty on the first render
Try this:
const List = ({ selectedHouse }) => {
const [houseTitles, setHouseTitle] = useState([]);
useEffect(() => {
const q = query(collection(db, "map-markers"));
onSnapshot(q, (querySnapshot) => {
setHouseTitle(
querySnapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
);
});
}, []);
return (
<div style={{ width: "50%" }}>
<ListItem title={houseTitles[selectedHouse]?.data?.title} />
{houseTitles
?.filter((title, index) => index !== selectedHouse)
?.map((title, index) => (
<ListItem key={index} title={title?.data?.title} />
))}
</div>
);
};
I want open modal window, when i doubleClick onRow in Table antd. And pass property to modal. Like this:
<Table
columns={columns}
components={components}
dataSource={dataSource}
onRow={(record) => {
return {
onDoubleClick: () => {
return <ModalWindow props={record} />
// or
return <ModalWindow> {record.name} </ModalWindow>
}
}
}} />
The problem is that no jsx is returned. I can save in state, like here:
const [visible, setVisible] = useState(false);
const [record, setRecord] = useState([]);
<Table
columns={columns}
components={components}
dataSource={dataSource}
onRow={(record) => {
return {
onDoubleClick: () => {
setRecord(record);
setVisible(true);
}
}
}} />
{visible &&
<ModalWindow record={record} />
}
but that's not my way. Help me, please.
You can handle your requirement by using one Modal and updating content of modal with double-click on every row, like this:
function App() {
// ...
const [isModalVisible, setIsModalVisible] = useState(false);
const [activeRecord, setActiveRecord] = useState(null);
const closeModal = () => {
setIsModalVisible(false);
};
return (
<>
<Table
columns={columns}
dataSource={data}
onRow={(record) => {
return {
onDoubleClick: () => {
setActiveRecord(record);
setIsModalVisible(true);
},
};
}}
/>
<Modal
title="User info"
visible={isModalVisible}
onCancel={closeModal}
footer={null}
>
{/* render whatever you want based on your record */}
<p>{activeRecord?.name} </p>
</Modal>
</>
);
}
I've implemented an example Here on stackBlitz, you can check it out.
This is my component of imageslider with button next and previous
I need som help how i can get individual img_src values and add them into another array and them use them in my image slider.
I welcome every solution corresponding to my aproach
const ImageSlider = () => {
const dispatch = useDispatch();
const ImageList = useSelector((state) => state.ImageList);
const { loading, error, Images } = ImageList;
useEffect(() => {
dispatch(ListImages());
}, [dispatch]);
var items = [Images.photos];
console.log(Images);
const classes = useStyles();
function Item(props) {
return (
<Paper>
{props.item.map(data => (
<img src={data.img_src} />
))}
{({ onClick, className, style, next, prev }) => {
return (
<Button onClick={onClick} className={classes.button} style={style}>
{next && "Next"}
{prev && "Previous"}
</Button>
);
}}
</Paper>
);
}
return (
<>
{loading ? (
<Loader />
) : error ? (
<h1>{error}</h1>
) : (
<Carousel>
{items.map((item, i) => (
<Item key={i} item={item} />
))}
</Carousel>
)}
</>
);
};
export default ImageSlider;
```
First of all you should move the Item component out of the ImageSlider. It is being redefined every render. You can use localState to keep track of the index.
const useImageIndexer = (maxIndex) => {
const [index, setIndex] = useState(0);
const nextImage = () => {
setIndex((current) => Math.min(maxIndex, current + 1));
};
const prevImage = () => {
setIndex((current) => Math.max(0, current - 1));
};
return [index, nextImage, prevImage];
}
Then to use inside the slider
const ImageSlider = () => {
const dispatch = useDispatch();
const ImageList = useSelector((state) => state.ImageList);
const photos = ImageList.Images.photos;
const [index, nextImage, prevImage] = useImageIndexer(photos.length);
const currentPhoto = photos[index];
// Further down in the code
if(loading) {
return (<Loader />);
}
if (error) {
return (<div>Oh no!</div>);
}
return (<div>
<img src={img.src} />
<button onClick={prevImage}>Previous</button>
<button onClick={nextImage}>Next</button>
</div>);
It seemed like you were wrapping photos which sounds like an array inside another array, that doesn't look right.
var items = [Images.photos];
Following Situation.
I have a functional Parent Component like this:
function TestAutomationTab() {
const theme = createMuiTheme({
typography: {
htmlFontSize: 10,
useNextVariants: true,
},
});
const [szenarios, setSzenarios] = useState([]);
const [filterSzenario, setFilterSzenario] = useState('ALL');
const [data, setData] = useState([{}]);
const [runAll, setRunAll] = useState(false);
const [runAllButton, setRunAllButton] = useState('RUN ALL');
useEffect(() => {
fetchDistinctSzenarios();
fetchTestfaelle();
}, []);
async function fetchDistinctSzenarios() {
const response = await Api.getDistinctTestautoSzenarios();
setSzenarios(response.data);
setSzenarios(oldState => [...oldState, 'ALLE']);
}
function handleFilterChange(event) {
setFilterSzenario(event.target.value);
fetchTestfaelle();
}
async function fetchTestfaelle() {
const response = await Api.getAllOeTestfaelle();
response.data.forEach((e) => {
e.status = 'wait';
e.errorStatus = '';
e.statusText = '-';
});
setData(response.data);
}
function sendSingleCase(id) {
data.forEach((e) => {
if(e.id === id){
e.status = 'sending';
}
})
}
return (
<React.Fragment>
<MuiThemeProvider theme={theme}>
<div style={styles.gridContainer}>
<Upload />
<TestautomationSzenarioFilter
/>
<DocBridgePieChart />
<div style={styles.uebersicht}>
{filterSzenario.length ? <OeTestfallAccordion
choosenFilter={filterSzenario}
testData={data}
runAll={runAll}
sendSingleCase={sendSingleCase}
/> : <div>Wähle Szenario</div>}
</div>
</div>
</MuiThemeProvider>
</React.Fragment>
);
}
OeTestfallAccordion
function OeTestfallAccordion(props) {
const data = props.testData;
return (
<React.Fragment>
{data.map(e => (<OeTestfall
key={e.id}
szenario={e.szenario}
testid={e.testfallid}
json={e.json}
status={e.status}
runAll={props.runAll}
errorStatus={e.errorStatus}
statusText={e.statusText}
sendSingleCase={props.sendSingleCase}
/>))}
</React.Fragment>
);
}
OeTestfall
function OeTestfall(props) {
const { szenario, testid, json } = props;
const [open, setOpen] = useState(false);
function handleOpen(event) {
event.stopPropagation();
setOpen(true);
}
function handleClose() {
setOpen(false);
}
return (
<ExpansionPanel>
<ExpansionPanelSummary expandIcon={<ExpandMoreOutlined />}>
<OeTestfallSummary
szenario={szenario}
testid={testid}
json={json}
status={props.status}
handleClose={handleClose}
handleOpen={handleOpen}
open={open}
statusText={props.statusText}
errorStatus={props.errorStatus}
sendSingleCase={props.sendSingleCase}
/>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<div>ForNoError</div>
</ExpansionPanelDetails>
<ExpansionPanelActions>
<Button
variant="outlined"
color="primary"
>
Bearbeiten
</Button>
<Button
variant="outlined"
color="secondary"
>
Löschen
</Button>
</ExpansionPanelActions>
</ExpansionPanel>
);
}
OeTestfallSummery
function OeTestfallSummary(props) {
const { handleOpen } = props;
const [status, setStatus] = useState('');
const [statusText, setStatusText] = useState('');
const [errorStatus, setErrorStatus] = useState('');
useEffect(() => {
setErrorStatus(props.errorStatus);
setStatusText(props.statusText);
setStatus(props.status);
}, []);
return (
<div style={styles.summaryWrapper}>
<Typography align="center" variant="subtitle1">
TestID: {props.testid}
</Typography>
<Typography align="center" variant="subtitle1" style={{ fontWeight: 'bold' }}>
{props.szenario}
</Typography>
<Button
size="small"
variant="outlined"
color="primary"
onClick={handleOpen}
>
JSON
</Button>
<Tooltip title="VorneTooltip" style={styles.lightTooltip} placement="left">
<Chip
color="secondary"
variant="outlined"
label={status}
/>
</Tooltip>
<StatusChip
status={errorStatus}
/>
<OeJsonViewer json={JSON.parse(props.json)} open={props.open} handleClose={props.handleClose} stopEventPropagation />
<Tooltip
title="ToolTipTitel"
style={styles.lightTooltip}
placement="top"
>
<Chip
color="primary"
variant="outlined"
label={statusText}
/>
</Tooltip>
<Button variant="contained" color="primary" onClick={() => props.sendSingleCase(props.testid)} >
Run
</Button>
<Button variant="contained" color="primary" onClick={() => console.log(status)} >
test
</Button>
</div>
);
}
In my OeTestfallAccordion the prop testData does not update. If i try to console.log it inside my childComponent it has the old Value like before i execute the sendSinglecase function. What do i need to do, that i update the Data correctly that my child component gets notified that the props had changed and it has to rerender.
EDIT:
I tried some new things and can narrow down the problem. In my TestAutomationTab Component i send the whole data State to the OeTestfallAccordion Child Component. In this OeTestfallAccordion Component i split up the Array of Data which consists of multiple Objects like:
0: {id: 41, testfallid: 1, json: "{\"testCaseData\":{\"baseData\":{\"Check\":\"Thing…e\":\"alle\",\"tuwid\":\"2909\"}},\"testType\":\"Test\"}}", ID: null, businessId: null, …}
1: {id: 42, testfallid: 2, json: "{\"testCaseData\":{\"baseData\":{\"testfallid\":\"1…e\":\"alle\",\"tuwid\":\"2909\"}},\"testType\":\"Test\"}}", edcomAuftragsId: null, businessId: null, …}
When i hit the function sendSingleCase in my Parent Component TestAutomationTab i just change one single Parameter of the Object. The whole construct of Data keeps the same. The Child Component doesnt recognize that i changed something in the Object of Data.
But i dont know why? I also tried to useEffect on Props change in my Child COmponent when the props are changed. But it never gets executed even tho some attributes got updated inside the props.data.
function OeTestfallAccordion(props) {
const testData = props.testData;
const [data, setData] = useState(testData);
useEffect(() => {
setData(testData);
console.log("triggered");
}, [props]);
...
}
Okay things worked out a bit.
I changed the sendSingleCase function to first Copy the whole state in a Temp variable. Change one Attribute inside an Object and then setData (inside useState) with the tempData Variable. So the whole State gets renewed and the child components recognize the change and rerender.
But it seems not to be very fast. Always to copy the whole Data in a new Variable and then reassign it is very Ressource heavy. Is there a better solution?
function sendSingleCase(id) {
const tempState = [...data];
tempState.forEach((e) => {
if (e.testfallid === id) {
e.status = "pressed";
console.log(e.status);
}
});
setData(tempState);
}
I want to add to Chip an startIcon={<Icon />}
when click on a Chip.
The state of the icon is managed by chipsState.
In this code,
the state of all chips would change.
How can I change only the chipsState of the element that is clicked?
In this code, the state of all chips will change.
How can I change only the chipsState of the element that is clicked?
const Modal:React.FC<Props>= (props) => {
const {modalData} = props;
const [chipsState, setChipsState] = useState(false);
const onChipClick = (element:any) => {
setChipsState(chipsState => !chipsState);
}
return (
<div>
{
modalData.symtoms.map((element:any, index:number) => (
<div key={index}>
<Chip onClick={() => onChipClick(element)} startIcon={chipsState && <Icon />}>{element.description}</Chip>
</div>
))}
</div>
);
}
export default Modal;
To handle local state (and better testing), you should create a new custom Chip component with dedicated chipState.
interface CustomChipProps {
description: string
}
const CustomChip = (props: CustomChipProps) => {
const [chipState, setChipState] = useState(false);
return <Chip onClick={() => setChipState(prev => !prev)} startIcon={chipState && <Icon />}>{props.description}</Chip>;
}
const Modal:React.FC<Props>= (props) => {
const {modalData} = props;
return (
<div>
{
modalData.symtoms.map((element:any, index:number) => (
<div key={index}>
<CustomChip description={element.description} />
</div>
))}
</div>
);
}
export default Modal;
You can achieve your desired output by changing chipState state from boolean to object.
So first let's change to object state instead of boolean
const [chipsState, setChipsState] = useState({});
Now we will change onChipClick function to change value of selected chip state
const onChipClick = (element:any) => {
setChipsState({...chipsState, chipsState[element]: !chipsState[element]});
}
And finally we will read correct value of each chipsState element.
<Chip onClick={() => onChipClick(element)} startIcon={chipsState[element] && <Icon />}>{element.description}</Chip>
You can try like the following
import React, { useState, useCallback } from "react";
import ReactDOM from "react-dom";
import { Grid, Row } from "react-flexbox-grid";
const ChipSet = ({ symtomsData }) => {
const data = symtomsData.map((symtom) => ({ ...symtom, isSelcted: false }));
const [chipSets, setChipSets] = useState(data);
const onSelectChipSet = useCallback(
(e, index) => {
const updatedChipSets = chipSets.map((chip, i) =>
i === index ? { ...chip, isSelcted: e.target.checked } : chip
);
setChipSets(updatedChipSets);
},
[chipSets]
);
console.log("chipSets", chipSets);
return (
<div>
<h1>Symtoms Data</h1>
{chipSets.map((x, i) => (
<div key={i}>
<label>
<input
onChange={(e) => onSelectChipSet(e, i)}
type="checkbox"
value={x.isSelcted}
/>
{x.description}
</label>
</div>
))}
</div>
);
};
class App extends React.Component {
render() {
const symtomsData = [
{
description: "mild"
},
{
description: "cold"
}
];
return (
<Grid>
<Row>
<ChipSet symtomsData={symtomsData} />
</Row>
</Grid>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));