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.
Related
I have a very basic react component like this
const Message = (props) => {
const [show, setShow] = useState(false);
return (
<p show={show}>My Message</p>
);
};
I want to use this component from another one, and I want to be able to show the first one by clicking on a button in the second one
const OtherComponent = (props) => {
return (
<>
<Message />
<Button onClick={setShow(true)}>Open Message</Button>
</>
);
};
of course this code does not work, is there a way to achieve this or is Redux my only option?
Move state to parent
const Message = ({ show }) => {
return (
<p show={show}>My Message</p>
);
};
const OtherComponent = (props) => {
const [show, setShow] = useState(false);
return (
<>
<Message show={show} />
<Button onClick={setShow(true)}>Open Message</Button>
</>
);
};
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 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
I'm trying to update the state of my component, but for some reason it keeps saying Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
I'm trying to have a modal that opens onclick of a <div>. Within that modal is a text input that will update the state (notes). For some reason it's saying invalid hook call - why is that?
const openTestModal = () => {
let [notes, setNotes] = useState("");
let [openModal, setOpenModal] = useState(true);
let modalBody =
<div>
<TextInput
value={notes}
onChange={(value) => setNotes(value)}
/>
</div>
return (
<Modal
open={openModal}
onCancel={() => setOpenModal(false)}
onConfirm={() => console.log('works')}
body={modalBody}
/>
)
};
const TestHooks = () => {
return (
<div onClick={() => openTestModal()}>
Test
</div>
)
};
Seems like you tried to render testModal in react as an event, which's not a way to go, at all. Instead you must render your testModal as component, like that, so click on Test div will open your modal:
const TestModal = () => {
const [notes, setNotes] = useState("");
const modalBody = (
<div>
<TextInput
value={notes}
onChange={(value) => setNotes(value)}
/>
</div>
)
return (
<Modal
open={openModal}
onCancel={() => setOpenModal(false)}
onConfirm={() => console.log('works')}
body={modalBody}
/>
)
};
const TestHooks = () => {
const [openModal, setOpenModal] = useState(false);
return (
<React.Fragment>
<TestModal openModal={openModal} setOpenModal={setOpenModal} />
<div onClick={() => setOpenModal(true)}>
Test
</div>
<React.Fragment>
)
};
Hope it helps :)
It's not working because your testHooks is calling to setState from a separate component. Add you testHooks code into your openTestModal component. It would work as is if TestHooks was a child of openTestModal as well.
Just started to learn about Reack hooks but I cannot figure out if it is possible to write a simple hook (or should I use some other approach, e.g. useEffect along with useState) in order to control visibility of multiple elements by clicking on different buttons on page.
Let's say I have a simple app with 2 buttons and 2 "modal" windows:
const App = () => {
const [firstModalOpen, toggleFirstModal] = useState(false);
const [secondModalOpen, toggleSecondModal] = useState(false);
return (
<div>
<button onClick={() => toggleFirstModal(true)}>Open First Modal</button>
<button onClick={() => toggleSecondModal(true)}>Open Second Modal</button>
<FirstModal
{...props}
show={firstModalOpen}
toggleModal={toggleFirstModal}
/>
<SecondModal
{...props}
show={secondModalOpen}
toggleModal={toggleSecondModal}
/>
</div>
)
}
const FirstModal = (props) => {
const { toggleModal, ...rest } = props;
return (
<Modal
{ ...rest }
show={firstModalOpen}
onHide={() => props.toggleModal(false)}
>
First modal content...
</Modal>
)
}
const SecondModal = (props) => {
const { toggleModal, ...rest } = props;
return (
<Modal
{ ...rest }
show={secondModalOpen}
onHide={() => props.toggleModal(false)}
>
Second modal content...
</Modal>
)
}
// state hook attempt
const useToggleModal = () => (init) => {
const [show, setToggleModal] = useState(init);
const toggleModal = () => setToggleModal(!show);
return { show, toggleModal };
};
Since those are react-bootstrap modal windows, they use show and onHide properties to determine/handle visibility and I have to pass rest prop to avoid some side-effects.
If I'd use my hook attempt in my app, I'd handle both modals on any button click so I came up with the idea to pass a string (to both, buttons and modals) which would tell which modal exactly to handle, but that approach for some reason looked a bit wrong.
Is there a "smarter" way in React to handle this internally instead of passing strings around?
If you have multiple modals and only one of them needs to open at once, then you must use a single state which stores which modal is opened, kind of like a string having the id of the modal. However if you want to open multiple modals, you would store the isOpen prop differently
For the first case you would write your code like
const App = () => {
const [openModal, toggleModal] = useState('');
return (
<div>
<button onClick={() => toggleModal('first')}>Open First Modal</button>
<button onClick={() => toggleModal('second')}>Open Second Modal</button>
<FirstModal
{...props}
show={openModal === 'first'}
toggleModal={toggleModal}
/>
<SecondModal
{...props}
show={secondModalOpen}
toggleModal={toggleModal}
/>
</div>
)
}
const FirstModal = (props) => {
const { toggleModal, ...rest } = props;
return (
<Modal
{ ...rest }
show={firstModalOpen}
onHide={() => props.toggleModal('first')}
>
First modal content...
</Modal>
)
}
const SecondModal = (props) => {
const { toggleModal, ...rest } = props;
return (
<Modal
{ ...rest }
show={secondModalOpen}
onHide={() => props.toggleModal('second')}
>
Second modal content...
</Modal>
)
}
For the second case it would be as you have written in your example, the only optimisation you can do for the second case is to store an array of modal objects and render them dynamically or let each modal handle its own toggle states and use useImperativeHandle to provide methods which parent can call to child modals like
const App = () => {
const firstRef = useRef(null);
const secondRef = useRef(null);
return (
<div>
<button onClick={() => this.firstRef.current.toggleModal()}>Open First Modal</button>
<button onClick={() => this.secondRef.current.toggleModal()}>Open Second Modal</button>
<FirstModal
{...props}
ref={firstRef}
/>
<SecondModal
{...props}
ref={secondRef}
/>
</div>
)
}
const FirstModal = forwardRef((props, ref) => {
const { showModal, toggleModal } = useToggleModal(false, ref);
return (
<Modal
{ ...rest }
show={showModal}
onHide={toggleModal}
>
First modal content...
</Modal>
)
})
const SecondModal = forwardRef((props, ref) => {
const { showModal, toggleModal } = useToggleModal(false, ref);
return (
<Modal
{ ...props }
show={showModal}
onHide={toggleModal}
>
Second modal content...
</Modal>
)
})
// state hook attempt
const useToggleModal = (init, ref) => {
const [show, setToggleModal] = useState(init);
const toggleModal = () => setToggleModal(!show);
useImperativeHandle(ref, () => ({
toggleModal
}))
return { show, toggleModal };
};