Passing children in react - javascript

this is my first time to develop a react application.
I wanted to display 5 buttons in a table. The first approach I did is to hardcode it in Table.js
Table.js
<Button hidden={controlButton} onClick={() => reprocessConfirmation()}>
Reprocess
</Button>{" "}
<Button hidden={controlButton} onClick={() => reprocessConfirmation()}>
View Details
</Button>
<Button hidden={!controlButton} onClick={() => updateConfirmation()}>
Update
</Button>{" "}
<Button hidden={!controlButton} onClick={() => deleteConfirmation()}>
Delete
</Button>{" "}
<Button hidden={!controlButton} onClick={() => resequenceConfirmation()}>
Resequence
</Button>
But Table.js is designed to be reusable, so I was tasked to move these to index.js. I tried it by creating a var that has the function containing the buttons and return it to Table.js and display it using children props using this line {this.children}
index.js
const testButton = () => {
return (
<React.Fragment>
<button>Test1</button>
<button>Test2</button>
<button>Test3</button>
<button>Test4</button>
<button>Test5</button>
</React.Fragment>
);
};
return (
<React.Fragment>
<MyTable
{...{
testButton
}}
/>
</React.Fragment>
);
}
In Table.js I added
table.js
export default function MyTable({
testButton,
...props
}) {
return (
<React.Fragment>
<Table {...getTableProps()}>
*****{props.children}
</Table>
</React.Fragment>
It seems that when I use
this.children= it is undefined
props.children= does not display anything
Thank you very much.

It seems like you didn't pass pageOptions prop to MyTable component as you render children based on it's length.

Related

How can I map over an array, and have a button access the mapped array without being in the map

Project idea: Essentially a stackoverflow clone. I have a "question" (in my case a code error), and you can post potential solutions to that error.
My issue is while mapping over the array of of "solutions", my button is inside the map so it gets duplicated with every solution. How can I move the button outside the map but with it still having access to the mapped information?
This is in React, hooks.
Code:
{props.errors?.map((error, index) => (
<Card
key={index}
title={error.id}
extra={More}
style={{ width: 500 }}
>
<p>{error.errorMessage}</p>
<p>{error.errorType}</p>
{error.solutions.map((solution, index) => {
return (
<>
<SyntaxHighlighter
language={error.errorType.toLowerCase()}
style={rainbow}
>
{solution.solution}
</SyntaxHighlighter>
<Button
type="primary"
onClick={() => {
updateOn();
addSolution(solution);
}}
>
Add Solution
</Button>
</>
);
})}
</Card>
))}

Dispatch a Redux action from a modal in React

I'm a begginer in React and I'm trying to find a way to dispatch a redux action from a modal.
I have a list of products and a button 'add to bag' under each image. When we click on the button 'add to bag', I want a modal to appear that ask for a confirmation. The action need to be dispatched when the user click on the confirm button inside de modal window.
The action need to grab the item object.
All is working fine ...but I'm not able to pass the item into the action when I want to launch the action from the modal.
So, my problem is not the Redux side but the modal part ( I use React-bootstrap for the modal).
I have this error message : 'item' is not defined
I'm not sure I understand exactly why this does'nt work and I failed to find a solution. I tried so many things but it's just not working.
Is there a simple way to add/pass the item data into the modal easily ?
Help would be very appreciated ! :)
Thanks a lot !!!
Here are part of my files :
Product.js
import { Modal } from "react-bootstrap";
function Products(props) {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div>
<CardGroup>
{_.map(props.data, (item) => (
<Col>
...Some code here
<div>
<div>
{item.name.map((item) => (
<ul key={item}>{item}</ul>
))}
</div>
<div>
<button onClick={() => {handleShow()}}>
Add to bag
</button>
</div>
</div>
</Col>
))}
</CardGroup>
<Modal show={show} onHide={handleClose}>
<Modal.Body>Please confirm you want to add this product</Modal.Body>
<Modal.Footer>
<button
onClick={props.addProductToBasket(item)}
>
Confirm
</button>
<button onClick={handleClose}>Cancel</button>
</Modal.Footer>
</Modal>
</div>
);
}
const mapStateToProps = (state) => {
return { code here ...};
};
const mapDispatchToProps = (dispatch) => {
return {
addProductToBasket: (id) => dispatch(addProductToBasket(id)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Products);
Here is the part of my Product.js file im my Store/actions folder
export const addProductToBasket = (id) => {
return {
type: ADD_PRODUCTD_TO_BASKET,
payload: id,
};
};
Since you only show the modal if an item was clicked, store the item or null instead of Boolean in the state, and open and close the modal accordingly:
function Products(props) {
const [selectedItem, setShowItem] = useState(null); // item for open model or null for closed
const handleClose = () => setShowItem(null);
const handleShow = item => setShowItem(item);
return (
<div>
<CardGroup>
{_.map(props.data, (item) => (
<Col>
...Some code here
<div>
<div>
{item.name.map((item) => (
<ul key={item}>{item}</ul>
))}
</div>
<div>
<button onClick={() => handleShow(item)}>
Add to bag
</button>
</div>
</div>
</Col>
))}
</CardGroup>
<Modal show={selectedItem !== null} onHide={handleClose}>
<Modal.Body>Please confirm you want to add this product</Modal.Body>
<Modal.Footer>
<button
onClick={() => props.addProductToBasket(selectedItem)}
>
Confirm
</button>
<button onClick={handleClose}>Cancel</button>
</Modal.Footer>
</Modal>
</div>
);
}
Not related, but it will make your life easier - use the object form of mapDispatchToProps that will save you the need to wrap with dispatch manually:
const mapDispatchToProps = {
addProductToBasket
};

How to show the dialog everywhere in the app on clicking a button using react and typescript?

i want to display a dialog on clicking additem and addbooks button using react and typescript.
what i am trying to do?
I want to display a dialog on clicking additem button or addbooks button . this dialog will have hide button. on clicking this hide button the dialog should never appear again for the session.
Below is the code,
function MainComponent () {
const [showDialog, setShowDialog] = React.useState(false);
const openDialog = () => {
setShowDialog(true);
};
const hideDialog = () => {
setShowDialog(false);
};
return (
<Route
path="/items"
render={routeProps => (
<Layout>
<Home
showDialog={showDialog}
openDialog={openDialog}
hideDialog={hideDialog}
{...routeProps}
/>
{showDialog && (
<Dialog
hideDialog={hideDialog}
/>
)}
</Layout>
)}
/>
<Route
path="/items/:Id/books/:bookId"
render={routeProps => (
<Layout>
<Books
openDialog={openDialog}
{...routeProps}
/>
{showDialog && (
<Dialog
hideDialog={hideDialog}
/>
)}
</Layout>
)}
</>
)
function Home ({openDialog}: Props) {
return (
<button Onclick={openDialog}>AddItem</Button>
)
}
function Books ({openDialog}: Props){
return (
<button onClick={openDialog}>AddBooks</Button>
)
}
function MessageDialog({hideDialog}: Props) {
return (
<button onClick={hideDialog}>hide</button>
)
}
Now the question is as you see i am rendering MessageDialog in two places based on showDialog value. if users clicks additems button the dialog is displayed and when user to navigates to other view and clicks addbooks button the dialog is displayed.
somehow i feel this is not the right approach or something is missing...
How can i create a global dialog component that is accessible anywhere from my app or using toastify or some better approach. could someone help me with this. thanks.
I think your code is pretty good, but I can think of two minor things that could potentially improve it.
Firstly, you have some duplicate code in your main component. You should be able to move the Dialog outside of the routes. Like so:
return (<>
{showDialog && (
<Dialog
hideDialog={hideDialog}
/>
)}
<Route ... />
<Route ... />
</>)
This will render your dialog no matter which route is matched.
Secondly, instead of passing openDialog and hideDialog callbacks as props, you could create a React context. This is optional, and dependning on the case this might not be desired. A React context is slightly more complex than just a callback, so it adds complexity to your code. The benefit is that the callbacks doesn't need to be passed down through the props of every child component, which makes the component structure cleaner if you have a large component tree.
To create a React context for this use case, you could do the following:
// Create a context with a default value of false
let DialogContext = React.createContext()
function MainComponent () {
...
return (<DialogContext.Provider value={{ setDialogOpen: (open) => setShowDialog(open) }}>
{showDialog && (
<Dialog />
)}
<Route ... />
<Route ... />
</ DialogContext.Provider>)
}
function Home ({openDialog}: Props) {
let dialogContext= useContext(DialogContext)
return (
<button onclick={() => dialogContext.setDialogOpen(true)}>AddItem</Button>
)
}
function Books ({openDialog}: Props){
let contextValue = useContext(DialogContext)
return (
<button onClick={() => dialogContext.setDialogOpen(true)}>AddBooks</Button>
)
}
function MessageDialog({hideDialog}: Props) {
let dialogContext= useContext(DialogContext)
return (
<button onClick={() => dialogContext.setDialogOpen(false)}>hide</button>
)
}

How do I get the id or index of a card in a list?

I'm having trouble getting the id's for documents I've pulled from a MongoDB database and then displayed on a series of cards I've rendered using React and Material-Ui.
I've tried console-logging id in my functions and in the API's that I've set up, but I either get 'undefined', which is what the API returns, or a bunch of meta-data, which is what the delete function returns.
Here is my delete function:
handleDelete = id => {
console.log(id);
console.log(this.props.match.params);
// API.deleteArticle(this.props.match.params.id)
// .then(res => console.log(res.data))
// .catch(err => console.log(err));
// this.props.history.push("/saved");
};
Here is my API:
deleteArticle: function(id) {
console.log(id);
return axios.delete("/api/articles/" + id);
}
And here is how my card is rendered:
{this.state.savedArticles.length ? (
<Grid>
{this.state.savedArticles.map((oneSavedArticle, i) => (
<Card
style={savedArticleCard}
key={oneSavedArticle.id}
>
<Typography variant="h5">{oneSavedArticle.headline}</Typography>
<Divider variant="middle" />
<Typography>{oneSavedArticle.snippet}</Typography>
<a href={oneSavedArticle.web_url} style={linkStyles}>
<Button style={buttonStyles}>READ IT</Button>
</a>
<button onClick={this.handleDelete}>Delete</button>
{/* <DeleteDialogue id={props.articleFromDatabase.id} {...this.props} /> */}
</Card>
))}
</Grid>
) : (
The other part of that Ternary is just a default card that displays if the db is empty.
I'd like to be able to get the id so I can then delete the document from the database (and the card from the DOM).
Any help will be greatly appreciated. Many thanks in advance!
The problem is that you are not passing the ID to the handelDelete function, you need to update your code as follows:
{this.state.savedArticles.length ? (
<Grid>
{this.state.savedArticles.map((oneSavedArticle, i) => (
<Card
style={savedArticleCard}
key={oneSavedArticle.id}
>
<Typography variant="h5">{oneSavedArticle.headline}</Typography>
<Divider variant="middle" />
<Typography>{oneSavedArticle.snippet}</Typography>
<a href={oneSavedArticle.web_url} style={linkStyles}>
<Button style={buttonStyles}>READ IT</Button>
</a>
<button onClick={() => this.handleDelete(oneSavedArticle.id)}>Delete</button>
{/* <DeleteDialogue id={props.articleFromDatabase.id} {...this.props} /> */}
</Card>
))}
</Grid>
) : (
You don't pass the id to the handleDeleteFunction() here:
<button onClick={this.handleDelete}>Delete</button>
Thats why id is always undefined if you log it in handleDeleteFunction().
You have two options to pass the id properly.
Option 1: Use arrow function
<button onClick={() => this.handleDelete(oneSavedArticle.id)}>Delete</button>
Option 2: Use bind()
<button onClick={this.handleDelete.bind(this, oneSavedArticle.id)}>Delete</button>

react-popper: re-position using scheduleUpdate

I'm using React-popper to show a date picker element after clicking a button.
JSX
<Manager>
<Reference>
{({ ref }) => (
<button ref={ref} onClick={this.onDateRangeBtnClick}>click to show</button>
)}
</Reference>
{ReactDOM.createPortal(
<Popper placement="auto-end" >
{({ ref, style, placement, arrowProps, scheduleUpdate }) => (
<div className={`dayPickerOverlay ${this.state.showDatePicker ? "" : "hidden"}`} ref={ref} style={style} data-placement={placement}>
<DateRangePicker />
</div>
)}
</Popper>,
document.querySelector('#root')
)}
</Manager>
When onDateRangeBtnClick is called after the button was clicked, I want to re-position the Popper element by calling scheduleUpdate method, but I do not know how to approach this.
How can I expose that specific scheduleUpdate to be called within the onDateRangeBtnClick or alternatively how can I conditionally call a function (scheduleUpdate for this matter) within JSX itself?
I would split the popper part into its own component and take advantage of the React lifecycle hooks.
Inside componentDidUpdate you can check if the open state changed, and trigger the scheduleUpdate accordingly.
// PopperElement.js
export default class PopperElement extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.open && this.props.open !== prevProps.open) {
this.props.scheduleUpdate();
}
}
render() {
return (
<div className={`dayPickerOverlay ${this.state.showDatePicker ? "" : "hidden"}`} ref={ref} style={style} data-placement={placement}>
<DateRangePicker />
</div>
);
}
}
// App.js
<Manager>
<Reference>
{({ ref }) => (
<button ref={ref} onClick={this.onDateRangeBtnClick}>click to show</button>
)}
</Reference>
{ReactDOM.createPortal(
<Popper placement="auto-end">
{({ ref, style, placement, arrowProps, scheduleUpdate }) => (
<PopperElement open={this.state.open} scheduleUpdate={scheduleUpdate} />
)}
</Popper>,
document.querySelector('#root')
)}
</Manager>
If you prefer a more concise approach, I think I'd use react-powerplug this way:
import { Manager, Popper, Reference } from 'react-popper';
import { State } from 'react-powerplug';
const App = () => (
<Manager>
<Popper>
{({ ref, style, scheduleUpdate }) => (
<State initial={{ open: false }} onChange={scheduleUpdate}>
{({ state, setState }) => (
<Fragment>
<Reference>
{({ ref }) => (
<button
ref={ref}
onClick={() => setState({ open: true }})
>click to show</button>
)}
</Reference>
{open && <YourContent ref={ref} style={style} />}
</Fragment>
)}
</State>
)}
</State>
</Manager>
);
I avoided to repeat the React.createPortal part for conciseness, it should be in place of YourContent.

Categories

Resources