New to JavaScript over the past two weeks and am attempting to build a table from a button click. Using react-bootstrap/Button and react-bootstrap-table-next libraries. I have three files that I'm working on:
File that holds the layout of the page
A ReactButton class
Table class
I can render the button on the page, but can't get the table to render on a click. On the layout page, I'm building the button with:
render() {
return (
<div>
<ReactButton
name="Reset Table"
onClick={this.buttonClick}
keyField={{keyField: 'keyField'}}
products={DataPopulation.sampleData()}
columns={DataPopulation.getFields()}
selectRowProp={{mode: 'checkbox'}}
/>
</div>
The button code is:
class ReactButton extends Component {
constructor(props) {
super(props);
this.state = {
clicked: false
};
this.buttonClick = this.buttonClick.bind(this);
}
async buttonClick() {
this.setState({
clicked: true
});
console.log(this.props);
console.log("Button part")
}
render() {
return (
<Button
variant="primary"
size="lg"
onClick={this.buttonClick}>
{this.props.name}
</Button>
)
}
}
export default ReactButton;
When I click the button, I can see the log of the properties (keyField, products, columns, etc) passed to the Button class. Is it possible to then pass these properties to a table class file and have it render the table? Table code with the react-bootstrap-table-next libraries would be something like this, I would imagine:
class MultiSelectTable extends Component {
constructor(props) {
super(props)
}
...
<BootstrapTable
keyField= { this.props.keyField }
data={ this.props.products }
columns={ this.props.columns }
selectRow={ this.props.selectRowProp }
/>
But I'm unsure how the flow should be to call this from the main layout page. Should I call the table build from the Button class? And how would I render the table?
Edit: Currently reviewing this for more info about it. It seems like the logic needs to be: the page renders the button, which when clicked renders the table. What is the preferred best practice for referencing and building hierarchical components?
You could do something like this in the parent component:
buttonClick(clicked) {
this.setState({
buttonClicked: clicked
})
}
render() {
return (
<>
<ReactButton
name="Reset Table"
onClick={this.buttonClick}
keyField={{keyField: 'keyField'}}
products={DataPopulation.sampleData()}
columns={DataPopulation.getFields()}
selectRowProp={{mode: 'checkbox'}}
/>
{this.state.buttonClicked ? <MultiSelectTable {...props} /> : null}
</>
)
}
And then in the button component you pass the click state back to the parent components click function:
buttonClick() {
this.props.onClick(true);
}
That way you make sure the table is rendered only when the button is clicked and all the props gets passed to the table component.
Related
I'm creating a kanban board. I'm trying to create a button that adds a new card.
So far I've been able to create a conditional with states that will essentially toggle a card. It'll appear when clicked and disappear when the button is clicked again, which is not what I want so I started over.
This is the function I want to call onClick. The message logs in the console when the button is clicked but the KanBanCard component is unresponsive and there are no errors.
newCard() {
console.log("New card being displayed");
return (
<div>
<KanBanCard />
</div>
)
}
My button rendering:
return (
<main>
<button onClick={this.newCard} className="card-add">
+
</button>
</main>
)
It's not rendering because you are not rendering the new <KanBanCard /> that you are creating. You cannot return it because it will return it to the onClick method. What you can do is create a state that holds the list of Kanban cards and update that when the button is clicked. Make sure you render that list. It would look like this:
class YourComponent extends React.Component {
constructor() {
this.state = {
kanbanList: [],
}
}
newCard() {
const newCard = <div><KanBanCard /></div>;
this.setState({ kanbanList: [...this.state.kanbanList, newCard] });
}
render() {
return (
<main>
<button onClick={this.newCard} className="card-add">+</button>
<div className="cards-holder">
{this.state.kanbanList.map(card => card)}
</div>
</main>
);
}
}
I am building an online store. When you click the checkout button, a sidebar slides into view showing the list of items in your cart. Inside of the cart is its list of items. You can change the quantity by toggling the up/down arrows in a Form.Control element provided by Bootstrap-React.
The way my code works is that when you toggle the up/down arrows to add or decrease the product quantity the state changes in the parent regarding what's in your cart. This triggers the child cart sidebar to close then reopen. I do not want this to happen! The sidebar should remain open.
I've tried two things; one is to use event.preventDefault() to try and make it so the page isn't refreshed, but this hasn't worked.
The other thing is trying to use shouldComponentUpdate and checking for whether the item quantity was changed, then preventing the app from re-rendering. This is the code I was using:
shouldComponentUpdate(nextProps, nextState) {
if (
nextState.cart &&
nextState.cart.length > 0 &&
this.state.cart.length > 0
) {
console.log("Next state cart num= " + nextState.cart[0].num)
console.log("curr state cart num= " + this.state.cart[0].num)
if (nextState.cart[0].num != this.state.cart[0].num) {
return false;
}
}
return true;
}
The problem is that my previous and future props are the same! Hence I can't write any code preventing re-rendering on item quantity change.
Can anyone provide some advice?
If your component is re rendering but its props and state aren't changing at all then you could prevent this with either React memo if you're using a function or if you're using a class based component then extending React.PureComponent instead of React.Component.
Both ways will do a shallow prop and state comparison and decide whether it should re render or not based on the result of said comparison. If your next props and state are the same as before then a re render will not be triggered.
Here's a codepen example so you can decide which one to use.
class App extends React.Component {
state = {
count: 0
};
handleClick = event => {
event.preventDefault();
this.setState(prevState => ({ count: prevState.count + 1 }));
};
render() {
return (
<div>
<span>Click counter (triggers re render): {this.state.count}</span>
<button style={{ marginLeft: "10px" }} onClick={this.handleClick}>
Click me to re render!
</button>
<SingleRenderClassComponent />
<SingleRenderFunctionComponent />
<AlwaysReRenderedClassComponent />
<AlwaysReRenderedFunctionComponent />
</div>
);
}
}
class SingleRenderClassComponent extends React.PureComponent {
render() {
console.log("Rendered React.PureComponent");
return <div>I'm a pure component!</div>;
}
}
const SingleRenderFunctionComponent = React.memo(
function SingleRenderFunctionComponent() {
console.log("Rendered React.memo");
return <div>I'm a memoized function!</div>;
}
);
class AlwaysReRenderedClassComponent extends React.Component {
render() {
console.log("Rendered React.Component");
return <div>I'm a class!</div>;
}
}
function AlwaysReRenderedFunctionComponent() {
console.log("Rendered function component");
return <div>I'm a function!</div>;
}
ReactDOM.render(<App />, document.getElementById("root"));
While becoming acquainted with React I stumbled upon the concept of portals in the developer documentation. However, I'm having difficulty understanding how this portal component actually renders on-demand, and how I can pass data to it to populate a modal.
Currently, I have two components that interact with each other: View.js and DataTable.js.
View.js:
const Example = (props) => {
console.log(props);
return (
<div>
<TopBar />
<DeploymentsHeader env={props.match.params.env} />
<PendingDataTable env={props.match.params.env} />
<DataTable env={props.match.params.env} />
</div>
);
}
Now for the DataTable component, a react-table is being rendered. When a user clicks on an individual row, my goal is to have a modal pop up (still unclear to me whether or not this needs to have its own separate component if I'm using React portals) and have it be populated with data that is already bound to the individual row (which I tested and have access too).
The code looks something like this:
<ReactTable
data={tableData}
filterable={true}
getTrProps={this.onRowClick}
columns={[
{
Header: "Header",
accessor: "service_name"
},
...
]}
/>
Now this is the function that gets passed to the table row props and executes on click:
onRowClick = (state, rowInfo) => {
return {
onClick: e => {
console.log('A Tr Element was clicked!');
console.log(rowInfo.original);
}
}
}
The data that I need is readily available to me in the object rowInfo.original. Now my question is: what is considered the 'correct' or 'best-practice' way to load a modal using a portal when an event such as this onClick trigger executes?
Do I need to have a separate Modal.js component that is actually a portal?
How do I get the data from this onRowClick function transported to this modal portal?
Thanks everyone.
You can conditionally render a portal as if it was just another React component. To start, you should separate the modal out into it's own component. Then, you can store the item id or item in state and toggle to let the modal know when to show or not.
onRowClick = (state, rowInfo) => {
return {
onClick: e => {
console.log('A Tr Element was clicked!');
console.log(rowInfo.original);
this.setState({
data: rowInfo.original,
showModal: true
});
}
}
}
render() {
return (
<ReactTable
data={tableData}
filterable={true}
getTrProps={this.onRowClick}
columns={[
{
Header: "Header",
accessor: "service_name"
},
...
]}
/>
{this.state.showModal && React.createPortal( <Modal data={this.state.data}>Your Data Goes Here</Modal>, document.getElementById('modal-portal')) }
)
}
EDIT:
They have a Modal example in their Portal docs that you should check out.
EDIT 2:
this.state.showModal is a piece of state that you need to add. You will use this to conditionally render the <Modal /> component (that you create). What I've done here is shorthand for:
if(this.state.showModal) {
return React.createPortal(...);
} else {
return null;
}
As for the actual <Modal /> component, you can make that however you want, you can use a react modal package, bootstrap modals or just build your own.
Example custom Modal.js:
const Modal = ({ children, data }) => (
<div className="my-modal">
{children}
// Here you can do stuff with data if you want
</div>
);
CSS:
.my-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
Note ReactDOM.createPortal its a function from react-dom not react
import {createPortal} from 'react-dom'
So I'm new to React and still learning, managing onClick from the parent element is easy but I'm not sure how I can then send that click up one or more steps to the parents of the parent. In my setup the components are like this Page > Header > Menu Button. The Header comp controls the menu opening and closing but for my animation purposes I need to set a state in the Page parent so it can pass it on to other components that are siblings of Header. When Menu Button is clicked I need to send it up the chain all the way to Page. Can someone please explain how this is achieved? Most examples I found just talk about a direct parent child onClick handle which I'm already doing with Header and Menu Button components.
See the following example...
Use a click handler that has been passed down through the react component inheritance chain.
function MenuButton(props) {
return <button onClick={props.onClick}>Do Something</button>
}
function Header(props) {
return (
<div>
<MenuButton onClick={props.onClick} />
</div>
)
}
Establish a click handler in the parent most component and pass as a prop to the child component like so...
class Page extends Component {
constructor() {
super();
this.state = {
someStateToCache: false
};
this.clickHandler = this.clickHandler.bind(this); //bind context to click handler
}
clickHandler() {
this.setState({ someStateToCache: true });
}
render() {
return (
<div>
<Header onClick={this.clickHandler} />
</div>
);
}
}
I'm attempting to make my own personal website, and trying to use React to do so. In the process, I intend to make each section a different React Component. My plan is to have the navbar at the top be able to select which component is currently "active", and actually gets rendered and shown. In addition, when switching to a new section, I would like the old component to have a "leaving" animation, and the new component to have an "entering" animation (these are done with react-motion). However, currently both the entering and leaving are done at the same time, because I'm changing the active state for both components at the same time. Is there any way to delay one component becomes active after another one becoming inactive?
The parent component that houses each section looks like so:
class Website extends React.Component {
constructor(props){
super(props)
this.state = {
homeActive: true,
aboutActive: false
}
homeActivator(){
this.setState({
homeActive: true,
aboutActive: false
})
}
aboutActivator(){
this.setState({
homeActive: false,
aboutActive: true
})
}
render(){
return (
<div>
<NavBar handleHome={this.homeActivator.bind(this)} handleAbout=
{this.aboutActivator.bind(this)}/>
<Home active={this.state.homeActive} />
<About active={this.state.aboutActive} />
</div>
}
And then one of the "sections" would look like so:
class Home extends React.Component{
render() {
let content = (
<div>
Home
</div>
)
if (!this.props.active){
return (
//Some jsx that results in the content leaving the page
)
}
return(
//Some jsx that results in the content entering the page
)
}
}
I did not have a ton of time to answer this, but came up with the best example I could. It's not an exact replica of what you are looking to do, but is very similar, so if you understand it, you will be able to figure out your problem quite easily.
To make things a little easier to understand, I am mimicking components with methods placed inside the React Class. Obviously in the real world, you would be importing your components from other files. I'm sure you'll understand what's going on.
export default class Example extends Component {
constructor(props) {
super(props)
this.state = {
c1: true,
c2: false
}
}
// Component One
renderc1() {
return (
<div>
I am component one
</div>
)
}
// Component Two
renderc2() {
return (
<div>
I am component two
</div>
)
}
changeComponents = () => {
this.setState({ c1: false })
setTimeout(() => {
this.setState({ c2: true })
}, 1500)
}
render() {
return (
<div className="example">
{this.state.c1 ? this.renderc1() : null}
{this.state.c2 ? this.renderc2() : null}
<button onClick={this.changeComponents}>Click me</button>
</div>
)
}
}
Clicking the button will fire off the changeComponents function, which will then immediately set the state of "c1" to false. A setTimeout after that ensures that component 2 will be delayed rendering to the screen.
Notice the arrow syntax, I used, which binds the this keyword to the class, so you don't have to worry about writing bind this everywhere.