I'm trying to find proper way to write the TableWrapper component. I want to be structured something like this when using it. Could you please help me how to pass the props from the parent to the children?
<TableWrapper
loadData={loadData}
data={data}
isExternalOpen={isExternalOpen}
setIsExternalCreate={setIsExternalCreate}
setIsExternalOpen={setIsExternalOpen}
showEmptyState={false}
limit={4}
loading={loading}
>
{({ dispatchHandler, isOpen, setIsOpen, data, previousPage, nextPage }: TableWrapperComponentInterface) => (
<>
<Table
title={title}
onCreateHandler={onCreateHandler}
columns={columns}
data={data}
previousPage={previousPage}
nextPage={nextPage}
actions={[actions]}
/>
<TableModal isOpen={isOpen} setIsOpen={setIsOpen} setIsCreate={setIsCreate}>
<>Doesn't matter</>
</TableModal>
</>
)}
</TableWrapper>
I have something like this but that doesn't seem right. I would be glad if somebody could help me. :)
export const TableWrapper = (props: TableWrapperInterface) => {
const {
children,
loadData,
setIsExternalCreate,
setIsExternalOpen,
emptyStateAddButtonTitle,
emptyStateButton = true,
emptyStateText,
showEmptyState,
loading,
data,
limit = 10,
isExternalOpen,
} = props
...
const Component = children
return (
<>
<div>
<Component
dispatchHandler={dispatchHandler}
setIsOpen={setIsExternalOpen}
isOpen={isExternalOpen}
data={sendData}
loadData={loadData}
previousPage={previousPage}
nextPage={nextPage}
/>
</div>
</>
)
}
export default TableWrapper
Related
I'm building out this project in react and using charts from syncfusion. I'm getting the error that's in the title of this post. It's only when I zoom in and out a bunch or if I close my side bar a few times. The amount of times needed to make this happen is completely random. Here's a few pieces of code
const SparkLine = ({id, height, width, color, data, type, currentColor}) => {
return (
<SparklineComponent
height={height}
width={width}
lineWidth={1}
valueType="Numeric"
fill={color}
border={{ color: currentColor, width: 2 }}
dataSource={data}
xName="xval"
yName="yval"
type={type}
tooltipSettings={{
visible: true,
format: '${xval} : data ${yval}',
trackLineSettings: {
visible: true
}
}}
>
<Inject services={[SparklineTooltip]} />
</SparklineComponent>
)
}
That's the component that returns SparklineComponent which is from the library #syncfusion/ej2-react-charts.
It's saying that it's the actual SparklineComponent that's in that library not any component that I made that's not returning anything. Here's the rest of the chain of code that leads to my index.js
<div className='mt-5'>
<SparkLine
currentColor='blue'
id='line-sparkline'
type='Line'
height='80px'
width='250px'
data={SparklineAreaData}
color='blue'
/>
</div>
This is just a snippet of code from a component called Ecommerce. It returns, at one point in the jsx, the SparkLine component from above. The next one is
<Routes>
{/* Dashbord */}
<Route path='/' element={<Ecommerce />} />
<Route path='/ecommerce' element={<Ecommerce />} />
That's my app.js which is wrapped by a context provider in my index.js
ReactDOM.render(
<ContextProvider>
<App />
</ContextProvider>
, document.getElementById('root')
);
The problem seems to be coming from the activeMenu variable from the state hook I have in my context provider which is used to open and close the side bar when you click the appropriate button or when you zoom in close(small screen size). Here's my ContextProvider
export const ContextProvider = ({children}) => {
const [activeMenu, setActiveMenu] = useState(true);
const [isClicked, setIsClicked] = useState(initialState);
const handleClick = (clicked) => {
setIsClicked({ ...initialState, [clicked]: true})
}
const [screenSize, setScreenSize] = useState(undefined)
console.log(`context provider ${activeMenu}${isClicked.userProfile}${initialState.userProfile}`);
return (
<StateContext.Provider value={{ activeMenu, setActiveMenu, isClicked,
setIsClicked, handleClick, screenSize, setScreenSize,}}>
{children}
</StateContext.Provider>
)
}
export const useStateContext = () => useContext(StateContext);
When I remove the activeMenu variable from app.js that I pull out from useStateContext which I've imported there everything works fine. I have absolutely no idea why.
The Solution is to use "Class Component" and extends "React.PureComponent"
import React from 'react'
import { SparklineComponent, Inject, SparklineTooltip } from '#syncfusion/ej2-react-charts'
export default class SparkLineChart extends React.PureComponent
{
render()
{
const { id, type, height, width, data, currentColor, color } = this.props;
return <SparklineComponent
id={id}
height={height}
width={width}
lineWidth='1'
valueType='Numeric'
type={type}
fill={color}
border={{ color: currentColor, width: 2 }}
dataSource={data}
xName='x-axis'
yName='y-axis'
tooltipSettings={{
visible: true,
format: 'X : ${x-axis} , Y : ${y-axis}',
trackLineSettings: { visible: true }
}}
>
<Inject services={[SparklineTooltip]} />
</SparklineComponent>
}
}
I am using react instant search library and my issue is that my custom refinement list components loses its selections when I open modal.
I control my modal with useState:
const [modalIsOpen, setModalIsOpen] = useState(false);
Everytime I call setModalIsOpen(true); the refinements reset.
My custom refinement list component:
const RefinementList = ({ items, refine }: RefinementListProvided) => {
// return the DOM output
return (
<div className="">
{items.map(({ value, label, count, isRefined }: any) => (
<div key={value}>
<motion.button
onClick={() => {
refine(value);
}}
className={``}
>
<div className="">
{label}
</div>
</motion.button>
</div>
))}
</div>
);
};
I connect it with connectRefinementList
const CustomRefinementList = connectRefinementList(RefinementList);
This is my main jsx:
<InstantSearch searchClient={searchClient} indexName="foods">
<CustomSearchBox />
<CustomRefinementList
transformItems={(items) => orderBy(items, "label", "asc")} // this prevents facets jumping
attribute="tags"
/>
<InfiniteHits hitComponent={Hit} cache={sessionStorageCache} />
<ModalForMealPreview
handleOpen={modalIsOpen}
handleClose={handleModalClose}
/>
</InstantSearch>
What can I do to persist state or prevent RefinementList component from rerendering?
Here is a basic Example of React.memo, this will help your code
import React, { useEffect, useState } from "react";
const MemoComp = React.memo(({ ...props }) => <Test {...props} />); // Main Area to watch
function ClassSearch() {
const [state, setState] = useState(1);
return (
<div>
<button onClick={() => setState(state + 1)}>Increase</button> <br />
<MemoComp data="memorized" /> <br />
<Test data="original" /> <br />
</div>
);
}
export default ClassSearch;
const Test = ({ data }) => {
const date = new Date().getTime();
return (
<>
Test {date} {data}
</>
);
};
I have a swipeable component with an open and close function that I would like to trigger from a child of the component. I don't think using state to do it in this instance would be correct since I want to avoid a re-render while the swipeable component is animating (please correct me if I'm wrong).
I'm using react-native-gesture-handler/swipeable and following their example here
SwipeCard component
import React, { useRef } from 'react';
import { RectButton } from 'react-native-gesture-handler';
import Swipeable from 'react-native-gesture-handler/Swipeable';
import Animated from 'react-native-reanimated';
const AnimatedView = Animated.createAnimatedComponent(View);
export const SwipeCard = ({ children }) => {
let swipeableRow = useRef(null);
let renderRightActions = (_progress, dragX) => {
let scale = dragX.interpolate({
inputRange: [-80, 0],
outputRange: [1, 0],
extrapolate: 'clamp',
});
return (
<RectButton style={styles.rightAction} onPress={close}>
{/* Change it to some icons */}
<AnimatedView style={[styles.actionIcon]} />
</RectButton>
);
};
let close = () => {
swipeableRow?.close();
};
let open = () => {
swipeableRow?.openRight();
};
return (
<Swipeable
ref={swipeableRow}
renderRightActions={renderRightActions}
friction={2}
rightThreshold={40}
>
{children}
</Swipeable>
);
};
Below is the component where I'm using SwipeCard and the Toggle is the event I want to use to fire the open() method in the SwipeCard component.
<Row>
{arr.map((item) => {
return (
<SwipeCard key={item.id}>
<CardContainer>
<CleaningCard
cleaningData={item}
/>
<Toggle onPress={() => {}}>
<Icon name="dots" />
</Toggle>
</CardContainer>
</SwipeCard>
);
})}
</Row>
You can use the render prop pattern and pass close, open as arguments.
Parent component where SwipeCard is used:
<Row>
{arr.map((item) => {
return (
<SwipeCard key={item.id}>
{({ close, open }) => (
<CardContainer>
<CleaningCard cleaningData={item} />
<Toggle
onPress={() => {
// close()/open()
}}
>
<Icon name="dots" />
</Toggle>
</CardContainer>
)}
</SwipeCard>
);
})}
</Row>;
SwipeCard component:
<Swipeable
ref={swipeableRow}
renderRightActions={renderRightActions}
friction={2}
rightThreshold={40}
>
{children({close, open})}
</Swipeable>
We're simply making the children a function that takes an object and returns the JSX. The required object is passed as an argument (children({close, open})).
Problem: cannot get ref to update from {current: null} to the actual ref on the component.
What i want to happen: {current: null}, as i understand it, should update to include the div that ref is on in order to be able to click ouside of it (eventually to close it). 9 understand that it does not update on first render, but it does not ever update. It does run twice on page load, both returning current: null.
What i tried: i have followed all the SO advice to use useEffect and then finally separating it into this function which appears to be the most appropriate and up to date method to do this. It just never updates current.
function useOutsideAlerter(ref) {
useEffect(() => {
function handleClickOutside(event) {
if (ref.current && !ref.current.contains(event.target)) {
console.log(ref);
} else {
console.log("else", ref);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
}
export const Modal = (props) => {
const [showModal, setShowModal] = useState(props.showModal);
const wrapperRef = useRef(null);
useOutsideAlerter(wrapperRef);
return (
<Layout>
<ModalOuter
showModal={showModal || props.showModal}
id={styles["modalOuter"]}
handleClose={props.handleClose}
>
<ModalInner
ref={wrapperRef}
handleClose={props.handleClose}
>
<Layout display="flex" flexDirection="column">
<Layout display="flex" flexDirection="column">
<ModalTitle title={props.title} />
</Layout>
<HR />
<Layout display="flex" flexDirection="column">
<ModalBody body={props.body} />
</Layout>
</Layout>
</ModalInner>
</ModalOuter>
</Layout>
);
};
ModalInner
export const ModalInner = (props) => {
return (
<Layout
id={props.id}
ref={props.ref}
display="flex"
justifyContent="center"
alignItems="center"
padding="2rem"
margin="2rem"
backgroundColor="white"
>
{props.children}
</Layout>
);
};
Layout Component
export const Layout = (props) => {
return (
<div
id={props.id}
ref={props.ref}
...
Issue
In React, there are a few special "props", ref and key are a couple of them. I put quotes around props because while they are passed as props, they are not passed on to or accessible on the props object in children components.
Solution
Use React.forwardRef to forward any passed React refs to functional components and expose them in children components.
export const ModalInner = React.forwardRef((props, ref) => { // <-- access ref
return (
<Layout
id={props.id}
ref={ref} // <-- pass ref *
display="flex"
justifyContent="center"
alignItems="center"
padding="2rem"
margin="2rem"
borderRadius="5px"
backgroundColor="white"
border={`1px solid ${Color.LightGray}`}
boxShadow={`0rem 0rem 1rem white`}
>
{props.children}
</Layout>
);
});
* Note: The Layout and children components will similarly need to forward the ref until you get to where it's actually attached to a DOMNode.
An alternative solution is to pass the ref as a normal prop.
<ModalInner
wrapperRef={wrapperRef}
handleClose={props.handleClose}
>
...
export const ModalInner = (props) => {
return (
<Layout
id={props.id}
wrapperRef={props. wrapperRef} // <-- pass wrapperRef prop
display="flex"
justifyContent="center"
alignItems="center"
padding="2rem"
margin="2rem"
borderRadius="5px"
backgroundColor="white"
border={`1px solid ${Color.LightGray}`}
boxShadow={`0rem 0rem 1rem white`}
>
{props.children}
</Layout>
);
};
Similarly, you need to drill the wrapperRef prop on through to children until you get to the actual DOMNode where you attach the ref.
Example
<div ref={props.wrapperRef> .... </div>
You may also find Refs and the DOM docs useful for working with React refs.
I am having an issue where I want an alert to popup when I click on an image with React. Unfortunately I cannot figure out why this does not work on when I click on the image. Can someone please help me with this issue?
state = {
cards
}
handleClick = () => {
alert("I have been clicked");
console.log("clicked")
}
render = () => {
return (
<Router>
<div className="App">
<Navbar />
<ImageContainer>
{this.state.cards.map(card => {
return <ImageCard handleClick = {this.handleClick} image={card.image} />
})};
</ImageContainer>
</div>
</Router>
);
}
}
Here is what I have for ImageCard:
export const ImageCard = (props) => (
<Col lg={4}>
<Card>
<Card.Img variant="top" src={props.image} />
</Card>
</Col>
);
You are passing a prop called handleClick to your <ImageCard /> component. Take a look at your <ImageCard />. Once it receives that prop it does nothing with it so it is not being used for an onClick in any way. Try renaming the prop from handelClick to onClick, or pass the handeClick down to the <Card /> or <Col /> component within <ImageCard />.