Modal element is not hiding [React & React - Bootstrap] - javascript

I've created modal with React Bootstrap. Despite the fact, that I'm using onHide function, nothing happens. Here's my modal:
<React.Fragment>
<Modal
{...this.props}
bsSize="large"
aria-labelledby="contained-modal-title-lg"
show={this.state.showModal}
onHide={this.handleHide}
>
<Modal.Body>
...
</Modal.Body>
<Modal.Footer>
<Button id = "closeModal" variant="danger" onClick=
{this.handleHide.bind(this)}>
Save and close
</Button>
</Modal.Footer>
</Modal>
</React.Fragment>
I'm passing "show" from other component, when click occurs on some element. onClick on that element is specified to: "showModal: true". Then I'm passing showModal to other component that is rendering different elements according to option choosed:
{this.state.addingState && (
<MyForm
{...this.state.item}
show={this.state.showModal}
...
/>
)}
At last in MyForm component I have function that passes props to component with modal:
createModalComponent {
...
let modalComponentProps= {
...
show: this.props.show,
}
So, this is the way "show" is going. In my file with modal component I have function for handling hide:
handleHide() {
this.setState({ showModal: false });
}
Now in this component showModal is initialize in state like so:
constructor(props) {
super(props);
this.state = {
showModal: this.props.show
};
this.handleHide = this.handleHide.bind(this);
}
I've tried many things. Other state variables, without initializing showModal in state and many more. When clicking on the button or beyond the modal, modal is still visible and not hiding. I will be very grateful for your help and/or suggestions how to fix this.
So, the way showModal is going: parent component (where this.state.addingState is happening) -> MyForm component (where let modalComponentProps= { show: this.props.show, ... happens) -> actual modal component
Code on CodePen

you have 2 possibilities: you can add the method to your parent and pass the method + the result of show like this (use same name of props and method for best practice, so you will be not confuse):
Parent
<Modal
{...this.props}
bsSize="large"
aria-labelledby="contained-modal-title-lg"
show={this.state.showModal}
handleHide={this.handleHide}
>
Child
<MyForm
show={this.props.showModal}
handleHide={this.props.handleHide}
/>
To use the modal from parent, you can call it like this in child: this.props.handleHide(true).
Or you let the child manage the state by itself, so you would place the method and state in child, not in parent (depending on the architecture).
It is not recommended to add the props in child state.
Also, you could use es6 function to avoid binding like this:
handleHide = () => this.setState({ showModal: false });

Look on the shouldComponentUpdate method
shouldComponentUpdate(nextProps) {
return !isEqual(this.props, nextProps);
}
You are checking only props but you are changing the state of the component. Fix the method or remove it and it will be working

Related

React Native not rendering on prop change

I have created the following component:
type ToggleButtonProps = { title: string, selected: boolean }
export default class ToggleButton extends Component<ToggleButtonProps>{
render(){
return (
<TouchableWithoutFeedback {...this.props}>
<View style={[style.button, this.props.selected ? style.buttonSelected : style.buttonDeselected]}>
<Text style={[style.buttonText, this.props.selected ? style.buttonTextSelected : style.buttonTextDeselected]}>{this.props.title}</Text>
</View>
</TouchableWithoutFeedback>
);
}
}
The styles are simple color definitions that would visually indicate whether a button is selected or not. From the parent component I call (item is my object):
item.props.selected = true;
I've put a breakpoint and I verify that it gets hit, item.props is indeed my item's props with a selected property, and it really changes from false to true.
However, nothing changes visually, neither do I get render() or componentDidUpdate called on the child.
What should I do to make the child render when its props change? (I am on React Native 0.59.3)
You can't update the child component by literally assigning to props like this:
item.props.selected = true;
However, there are many ways to re-render the child components. But I think the solution below would be the easiest one.
You want to have a container or smart component which will keep the states or data of each toggle buttons in one place. Because mostly likely, this component will potentially need to call an api to send or process that data.
If the number of toggle buttons is fixed you can simply have the state like so:
state = {
buttonOne: {
id: `buttonOneId`,
selected: false,
title: 'title1'
},
buttonTwo: {
id: `buttonTwoId`,
selected: false,
title: 'title2'
},
}
Then create a method in the parent which will be called by each child components action onPress:
onButtonPress = (buttonId) => {
this.setState({
[buttonId]: !this.state[buttonId].selected // toggles the value
}); // calls re-render of each child
}
pass the corresponding values to each child as their props in the render method:
render() {
return (
<View>
<ToggleButton onPressFromParent={this.onButtonPress} dataFromParent={this.state.buttonOne} />
<ToggleButton onPressFromParent={this.onButtonPress} dataFromParent={this.state.buttonTwo} />
...
finally each child can use the props:
...
<TouchableWithoutFeedback onPress={() => this.props.onPressFromParent(this.props.dataFromParent.id)}>
<View style={[style.button, this.props.dataFromParent.selected ? style.buttonSelected : style.buttonDeselected]}>
...
I left the title field intentionally for you to try and implement.
P.S: You should be able to follow the code as these are just JS or JSX.
I hope this helps :)
Because children do not rerender if the props of the parent change, but if its STATE changes :)
Update child to have attribute 'key' equal to "selected" (example based on reactjs tho')
Child {
render() {
return <div key={this.props.selected}></div>
}
}

React: Do children always rerender when the parent component rerenders?

It is to my knowledge that if a parent component rerenders, then all its children will rerender UNLESS they implement shouldComponentUpdate(). I made an example where this doesn't seem to be the true.
I have 3 components: <DynamicParent/>, <StaticParent/> and <Child/>. The <Parent/> components are responsible for rendering the <Child/> but do so in different ways.
<StaticParent/>'s render function statically declares the <Child/> before runtime, like so:
<StaticParent>
<Child />
</StaticParent>
While the <DynamicParent/> handles receiving and rendering the <Child/> dynamically at runtime, like so:
<DynamicParent>
{ this.props.children }
</DynamicParent>
Both <DynamicParent/> and <StaticParent/> have onClick listeners to change their state and rerender when clicked. I noticed that when clicking <StaticParent/> both it and the <Child/> are rerendered. But when I click <DynamicParent/>, then only the parent and NOT <Child/> are rerendered.
<Child/> is a functional component without shouldComponentUpdate() so I don't understand why it doesn't rerender. Can someone explain why this is to be the case? I can't find anything in the docs related to this use case.
I'll post your actual code for context:
class Application extends React.Component {
render() {
return (
<div>
{/*
Clicking this component only logs
the parents render function
*/}
<DynamicParent>
<Child />
</DynamicParent>
{/*
Clicking this component logs both the
parents and child render functions
*/}
<StaticParent />
</div>
);
}
}
class DynamicParent extends React.Component {
state = { x: false };
render() {
console.log("DynamicParent");
return (
<div onClick={() => this.setState({ x: !this.state.x })}>
{this.props.children}
</div>
);
}
}
class StaticParent extends React.Component {
state = { x: false };
render() {
console.log("StaticParent");
return (
<div onClick={() => this.setState({ x: !this.state.x })}>
<Child />
</div>
);
}
}
function Child(props) {
console.log("child");
return <div>Child Text</div>;
}
When you write this code in your Application render:
<StaticParent />
What's rendered is this:
<div onClick={() => this.setState({ x: !this.state.x })}>
<Child />
</div>
And in reality, what happens (roughly) is this:
function StaticParent(props) {
return React.createElement(
"div",
{ onClick: () => this.setState({ x: !this.state.x }) },
React.createElement(Child, null)
);
}
React.createElement(StaticParent, null);
When you render your DynamicParent like this:
<DynamicParent>
<Child />
</DynamicParent>
This is what actually happens (again, roughly speaking)
function DynamicParent(props) {
return React.createElement(
"div",
{
onClick: () => this.setState({ x: !this.state.x }),
children: props.children
}
);
}
React.createElement(
DynamicParent,
{ children: React.createElement(Child, null) },
);
And this is the Child in both cases:
function Child(props) {
return React.createElement("div", props, "Child Text");
}
What does this mean? Well, in your StaticParent component you're calling React.createElement(Child, null) every time the render method of StaticParent is called. In the DynamicParent case, the Child gets created once and passed as a prop. And since React.createElement is a pure function, then it's probably memoized somewhere for performance.
What would make Child's render run again in the DynamicParent case is a change in Child's props. If the parent's state was used as a prop to the Child, for example, that would trigger a re-render in both cases.
I really hope Dan Abramov doesn't show up on the comments to trash this answer, it was a pain to write (but entertaining)
It's mainly cause of you have 2 different "children".
this.props.children
<Child/>
They're not the same thing, first one is a prop passed down from Application -> DynamicParent, while the second one is a Component rendered in StaticParent, they have separate rendering/life cycles.
Your included example
class Application extends React.Component {
render() {
return (
<div>
{/*
Clicking this component only logs
the parents render function
*/}
<DynamicParent>
<Child />
</DynamicParent>
{/*
Clicking this component logs both the
parents and child render functions
*/}
<StaticParent />
</div>
);
}
}
Is literally the same as:
class Application extends React.Component {
render() {
// If you want <Child/> to re-render here
// you need to `setState` for this Application component.
const childEl = <Child />;
return (
<div>
{/*
Clicking this component only logs
the parents render function
*/}
<DynamicParent>
{childEl}
</DynamicParent>
{/*
Clicking this component logs both the
parents and child render functions
*/}
<StaticParent />
</div>
);
}
}
As a comment to SrThompsons answer: "What would make Child's render run again in the DynamicParent case is a change in Child's props. If the parent's state was used as a prop to the Child, for example, that would trigger a re-render in both cases."
So props or not props passed to child component however it may look will cause a rerender if parent rerenders (so use React.memo for a child without props :) )
"Whether you’re implementing your component as a class component that extends React.Component, or as a functional component, the render function is called again whenever the parent container renders again." Please read here for more info, because great article. https://medium.com/free-code-camp/yeah-hooks-are-good-but-have-you-tried-faster-react-components-e698a8db468c"
It will only re-render components that have had a change. If nothing on the child component has changed, it will not be re-rendered.

Dynamically Creating a React Modal Using Portals

How can I append a component to a modal using a nested portal?
I am building a page that displays several modals. I want to reuse the same modal but when a button is clicked, the app loads different content into the modal.
WIP CodePen:
https://codepen.io/jtsharpsite/pen/gorvjR
render() {
return ReactDOM.createPortal(
this.props.children,
domNode,
);
}
I have it pretty close to how I think it might work, but I cannot figure out how to append another component to an already appended modal component.
I have a button which calls a handler that opens the modal and specifies the component type.
<button onClick={this.handleShow.bind(this, "p3009", "product")}>
Product 3009
</button>
<button onClick={this.handleShow.bind(this, "s1", "special")}>
Special #1
</button>
The handler is in the App context and opens the modal sibling:
handleShow(modalId, modalType) {
this.setState({ showModal: true });
}
When the modal component mounts, I then try to append the Product when it mounts.
componentDidMount() {
//TODO how to append to parent modal?
modalRoot.appendChild(this.el);
}
How can I move the <Product> or the <Special> up into the <Modal>?
Here's an example of a reusable modal form... Theres a lot here you don't need, but just pay attention to the handleSubmit. Within the handleSubmit we call onComplete() which can be any function we pass through to the reusable modal when we call it, based on what function we want our modal to do... in our case it was capturing username, password, etc.
class ReusableModalForm extends React.Component {
constructor(props){
super(props);
this.state ={
};
}
handleChange(e) {
let {name, value} = e.target;
this.setState({
[name]: value,
usernameError: name === 'username' && !value ? 'username must have a value' : null,
emailError: name === 'email' && !value ? 'email must have a value' : null,
passwordError: name === 'password' && !value ? 'password must have a value' : null,
});
}
handleSubmit(e) {
e.preventDefault();
this.props.onComplete(this.state)
}
render() {
return (
<Modal
open={this.state.createAccountModalOpen}
trigger={<Link size="m" theme="bare" href="#" className="main-menu-item" onClick={this.handleSubmit}>{this.props.buttonText}</Link>}
closeIcon
onClose={() => { this.setState({
createAccountModalOpen: false,
}); }}
>
<Header icon='add user' content='Create account' />
<Modal.Content>
<Form />
</Modal.Content>
<Modal.Actions>
<Button color='green' onClick={this.handleSubmit}>
<Icon name='add user' /> {this.props.buttonText}
</Button>
</Modal.Actions>
</Modal>
);
}
}
export default ReusableModalForm;
Based on this logic, you could construct a modal with a series of "trigger texts" to only render certain things based on what kind of props are passed down when you use your reusable modal.
Example.
<ReusableModal triggerText={'showAdress'} onComplete={this.showUsersHomeOnGoogle} />
Then in your reusableModal somewhere....
{this.props.triggerText === showAdress ? this.setState=({showHomeAdressPortion: true)}
UPDATES
class App extends React.Component {
constructor(props) {
super(props);
this.state = { showModal: false };
this.handleShow = this.handleShow.bind(this);
this.handleHide = this.handleHide.bind(this);
}
handleShow(modalId, modalType) {
console.log(
"Append content with ID: " + modalId + " of component type: " + modalType
);
//Here you should do a "fetch" first and grab your entire product information based on chosen ID and store it to state as an object...
//setState({activePortal: someContentObject})
//Then... I would use promise or aync await, show modal...
//Have Modal display from this state object
this.setState({ showModal: true, activePortal: });
//if it is a product
if (modalType == "product") {
//fetch additional images and add to state
//add images to {product.additionalPics}
}
//TODO re-render the modal with the appending element portal
}
handleHide() {
this.setState({ showModal: false });
}
render() {
//console.log("RENDER App");
// Show a Modal on click.
const modal = this.state.showModal ? (
<Modal>
<div className="modal">
<div className="modal-content-wrapper">
{/* I think what you're asking to do here is display basically "any" information that comes from a product fetch. This isnt possible without templeting somehow.. You will have to hard code like "title" "body" "image" into this modal...
<h1>this.state.activeProduct.title</h1>*/}
{/* CONTENT NEEDS TO GO HERE SOMEHOW */}
{this.props.children}
</div>
<button onClick={this.handleHide}>Hide modal</button>
</div>
</Modal>
) : null;
Have you tried using a parent-child pattern in the portal?
For example, make your children like this and just have ModalDisplay render its children. I don't know if what you are trying to do is possible because I think it requires portals to support fragments and I'm not sure if they do.
I don't know if I got the context of your question right but the idea is the same you have a single child that itself has children instead of having more than 1 child in the portal call.
<ModalDisplay>
<Special title="special" text="text" />
<Product title="product" pic="picture" />
</ModalDisplay>

React app - IconMenu not expanded on Click event.

I need help with react app and IconMenu from material-ui.
Studying many similar issues without results :(
Having following code, I want to trigger menu expand manually - I need it in tests framework to simulate clicking on submenu item.
const Menu = () => {
return (
<IconMenu
iconButtonElement={<FlatButton style={{height: '100%'}
}
onClick={() => console.log('clicked!')}
label={'FooLabel'}
labelPosition='before'
/>}>
<MenuItem primaryText="submenu" leftIcon={<SignOutIcon />} />
</IconMenu>
);
};
The problem is that, when I do click on menu item, the onClick event is triggered, but menu is not expanded at all:
demo: https://imgur.com/32RzHcB
I was trying to send custom event by dispatchEvent function, but even onClick is not triggered.
Is something what i missed?
It looks like by using onClick you override the default behaviour, so you'll need to use IconMenu's open prop and tell it when you want it to be open (true) or closed (false).
You'll need to have some code in your component that toggles open between true and false. To do this, the component will need to be stateful instead of functional. If you haven't done this before, check out the react docs on converting a function to a class
Try:
class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {
menuOpen: false
};
}
_toggleOpen = () => {
this.setState({
menuOpen: !this.state.menuOpen
});
};
_handleClick = () => {
this._toggleOpen();
// .. do your other on click stuff here
}
render(
<IconMenu
iconButtonElement={<FlatButton style={{height: '100%'}
}
onClick={this._handleClick}
label={'FooLabel'}
labelPosition='before'
open={this.state.menuOpen}
/>}>
<MenuItem primaryText="submenu" leftIcon={<SignOutIcon />} />
</IconMenu>
);
}
so now we've got a state that the Menu component can update which will decide whether the menu should be open and which gets passed down into the open prop.

ReactJS - button onClick gets called during render

I have a form with type="range". Now I would like to add 3 buttons that change the same value that the form does. For some reason, the buttons onClick event seems to get called repeatedly upon calling the render function.
This is my component:
class Slider extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleButton = this.handleButton.bind(this);
}
handleChange() {
this.props.onSetCountdown(parseInt(this.refs.seconds.value, 10));
}
handleButton(value) {
this.props.onSetCountdown(parseInt(value, 10));
}
render() {
return(
<div>
<form className={styles.ttSlider} ref="form">
<input max="480" min="60" name="slider" onChange={this.handleChange} ref="seconds" type="range" value={this.props.totalSeconds}/>
<button onClick={this.handleButton(60)}>1min</button>
<button onClick={this.handleButton(180)}>3min</button>
<button onClick={this.handleButton(300)}>5min</button>
</form>
</div>
)
}
}
Slider.propTypes = {
totalSeconds: React.PropTypes.number.isRequired,
onSetCountdown: React.PropTypes.func.isRequired
};
And this is from the parent component:
handleSetCountdown(seconds) {
this.setState({
count: seconds
});
}
From the parent component render:
<Slider totalSeconds={count} onSetCountdown={this.handleSetCountdown}/>
This is the error that I get:
Warning: setState(...): Cannot update during an existing state
transition (such as within render or another component's
constructor). Render methods should be a pure function of props and
state; constructor side-effects are an anti-pattern, but can be moved
to componentWillMount.
To me this looks like the buttons onClick gets called while the component is still rendering. What am I doing wrong?
It's because instead of passing the function to the event onClick, you're calling the function directly.
Try doing it this way:
<button onClick={() => { this.handleButton(60)}}>1min</button>
<button onClick={() => { this.handleButton(180)}}>3min</button>
<button onClick={() => { this.handleButton(300)}}>5min</button>
Found the answer here: React onClick function fires on render
Hope it helps!
If you dont want to use anon functions for any reason, the second method is to use bind directly at render function. Then you can delete lines at your constructor :)
<button onClick={this.handleButton.bind(this, 60)}>1min</button>

Categories

Resources