I am trying to add authentication to my react redux app, and I want to use modals for the login and signup pages. I have a LoginForm and a SignupForm which are static components, and I have LoginPage and SignupPage, which are my container components for the forms.
I am currently able to render the container components using login and signup routes, but I want switch to using modals.
Please how do I do this using a modal, say, react-materialize modal or normal materialize css modal?
Thanks for the help.
Revisiting this answer, I would say you need to have two components able to communicate. This is the plain React way to do this. Notice there is one App component with two sibling components. By passing a props value to the Modal component and a props callback to its sibling, we can get the modal to render or not render.
class Modal extends React.Component {
render() {
return this.props.modalShow ? (
<div className="modal">
Modal loaded
</div>
) : null;
};
};
class Sibling extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.openModal();
}
render() {
return (
<div>
I am a sibling element.
<button onClick={this.handleClick}>Show modal</button>
</div>
)
};
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
modalShow: false,
};
this.openModal = this.openModal.bind(this);
};
openModal() {
this.setState({ modalShow: true });
}
render() {
return (
<div>
<Modal modalShow={this.state.modalShow} />
<Sibling openModal={this.openModal}/>
</div>
)
};
};
ReactDOM.render(
<App />,
document.getElementById('app')
);
From here, using Redux is much simpler since the state is "global" already. But the idea is the same. Instead of having a parent control the state, just use your reducers and actions. I hope this helps.
Related
I need a little help. I'm working on a project that uses Class Components in React and I got stuck with a issue.
How can I pass datas using props?
For example, imagine that I have one Component that have an array in the state:
import React,{Component} from "react";
class CarList extends Component{
constructor(props){
super(props);
this.state = {
carList: ['Jeep', 'Kwid','HB20','Ônix', 'Prisma', 'Gol quadrado']
}
}
render(){
return(
<div>
</div>
);
}
}
export default CarList;
And now I have to call this array in a Option Tag inside a Select Tag.
Let's imagine this component Bellow:
import React from "react";
import { Component } from "react";
import CarList from "./components/Datas";
class App extends Component{
render(){
return(
<div>
<p>I got it! Here is the car list:</p>
<select>
{this.state.CarList.map( (item,x)=>{
return(
<option key={x}>{item}</option>
)
})}
</select>
</div>
)
}
}
export default App;
This piece of code does not work.
the console.log says: "Uncaught TypeError: this.state is null"
I know that I could create a div with my datas and call with , but I have to use props to pass the datas between the Components.
How can I create a callback function using props to resolve this?
Hi!
I tried to call using this.state, but I got "this.state is not defined"
To pass your data as a props you have to pass it to your child component like this from your parent component :
class ParentComponent extends React.Component {
state = {
carList: [],
};
constructor(props) {
super(props);
this.state = {
carList: ['Jeep', 'Kwid', 'HB20', 'Ônix', 'Prisma', 'Gol quadrado'],
};
}
render() {;
return (
<div>
<ChildComponent carList={this.state.carList || []} />
</div>
);
}
}
and then it is accessible in your child component with this.props.
you can use this props in child component like this:
class ChildComponent extends React.Component {
render() {
return (
<div>
{this.props.carList.map((cars, index) => {
return (
<span key={index}>
{cars}
<br />
</span>
);
})}
</div>
);
}
}
Edit -
if you want to see source code : https://stackblitz.com/edit/react-ts-ddcylu?file=Parent.tsx
I have 2 classes to provide the modal-dialog functionality:
import React from 'react'
import Modal from 'react-modal'
export default class ModalBase extends React.Component {
state = { show:false }
handleOpen = opts => {
this.setState( { ...opts, show:true } )
console.info( 'ModalBase handleOpen', this.constructor.name, 'show', this.state.show )
}
handleClose = () => this.setState( { show:false } )
render() {
console.info( 'ModalBase render show', this.state.show )
return <Modal isOpen={this.state.show} onRequestClose={this.handleClose} className="Modal" overlayClassName="Overlay">
{this.props.children}
</Modal>
}
}
and
export default class InfoPopup extends ModalBase {
state = { ...this.state, tech:{} }
render() {
console.info('InfoPopup render show', this.state.show)
return (
<ModalBase>
<div/><div/>
</ModalBase>
)
}
}
When I call InfoPopup.handleOpen({a:42}), the following shows up in the console:
ModalBase handleOpen InfoPopup show true
InfoPopup render show true
ModalBase render show false
so, the ModalBase's state.show is not changed and hence the popup is not shown.
How shall I properly propagate the state to enclosing parent object?
TIA
Use composition instead of inheritance
From the React docs:
React has a powerful composition model, and we recommend using composition instead of inheritance to reuse code between components.
See: https://reactjs.org/docs/composition-vs-inheritance.html
So export default class InfoPopup extends ModalBase is not advised.
1. Let InfoPopup render ModalBase but keep track of open/close state
You could turn it around and have a generic BaseModal component for modal styling that you pass props such as title and content. The InfoPopup keeps track of the opened/closed state. From the same React docs page:
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Sign Me Up!
</button>
</Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
2. Render ModalBase and pass the type of modal as prop
You could also always render ModalBase for an info, warning, error modal etc. Then you pass the type of modal as prop to ModalBase. ModalBase determines some specifics based on that type prop.
3. use a render prop
Described here: https://reactjs.org/docs/render-props.html
Let ModalBase accept a function as children prop.
So in InfoPopup:
<ModalBase>
{({ toggle }) => (
<button onClick={toggle} />
)}
</ModalBase>
And in ModalBase:
render() {
return <Modal ...>{this.props.children({ toggle: this.openOrClose })}</Modal>
}
4. Pass a component to ModalBase to render when open
A bit of a variant on 2. You could also pass a component as prop to ModalBase that it should show when it's open.
<ModalBase
modalContent={<InfoPopup />}
/>
In my situation I have a root component called App.
Inside my App component render function I have two other react components. ModalWrapper is the first component and it has a method called closeModal() which I would like to be able to call from within its child component AddUser. Is there anyway to pass this method down to addUser without moving the AddUser call to the ModalWrapper render function?
render() {
return (
<div>
<ModalWrapper title="Add Member" buttonText="Add Member">
<AddUser />
</ModalWrapper>
<div>
)
}
class Parent extends Component {
constructor(props){
super(props);
this.closeModal = this.closeModal.bind(this);
}
closeModal(){
alert('close Modal');
}
render(){
return(
<div>
<ChildComponent closeModal = {this.closeModal} />
</div>
);
}
}
class ChildComponent extends Component {
constructor(props){
super(props);
}
componentDidMount(){
this.props.closeModal();
}
render(){
return(<div> </div>);
}
}
Let's say you have parent component and you define a function in parent component and want to use that function in to the child component. You can use parent component function in to child component by passing it via props. and call parent function in child component via props.
I have an issue with React Router v.4. When a route is matched in the parent component and the child component is rendered, the componentDidMount method of the child triggers the showAlbum method passed to it by the parent.
However, though the showAlbum method is triggered, the setState method inside it does not update the state of the parent. When the child component is unmounted, the showAlbum method works correctly, just as it does on the subsequent calls.
Any idea where do I go wrong?
Thank you!
Parent component:
export default class AlbumContainer extends Component {
constructor(props) {
super(props)
this.state = {
showAlbum: false
}
}
showAlbum() {
this.setState({
showAlbum: !this.state.showAlbum
})
}
render() {
return (
<section className="border">
<div className="u-innerContainer">
<Route path='/:linkName' render={ (props)=><Album showalbum={ this.showAlbum.bind(this) }/> } />
</div>
</section>
)
Child component:
export default class Album extends Component {
render() {
return (
<section>
<div className="u-innerContainer">
<Link to="/">Back</Link>
<h3>{ 'title' }</h3>
<section>{ 'content' }</section>
</div>
</section>
)
}
componentDidMount() {
this.props.showalbum()
}
componentWillUnmount() {
this.props.showalbum()
}
}
I am sorry, I haven't got time to verify the solution, but your problem can be caused by setting of state based on previous state value.
https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
Try to set new state by this way:
showAlbum() {
this.setState(prevState => ({
showAlbum: !prevState.showAlbum
}));
}
Please add constructor in Child component :
constructor(props) {
super(props)
}
I want to render BlackSpark when RedSpark is clicked, but I'm not sure how to change the state of a component in another component. I know how to set state in the component itself, but how do I affect another component when I click a different component?
class BlackSpark extends React.Component {
render() {
return (
<div className="black"></div>
);
}
}
class RedSpark extends React.Component {
render() {
return (
<div className="red"></div>
);
}
}
class App extends React.Component {
render() {
return (
<div>
<BlackSpark />
<RedSpark />
</div>
);
}
}
In React, there's a concept of component composition as you've already embraced -- it allows you to accomplish what you want by rendering children based on the parent's state, another key concept known as lifting state up. What this means, is if you have mutually dependent components, create a single parent which composes them, and have state in the parent control the presentation and logic of the children. With the parent App, you can keep your state inside App, and based on App's state, conditionally render whatever you want -- either BlackSpark or both. For example, using the logical && operator:
{condition && <Component />}
This will only render <Component> when condition is truthy, or else it will not render anything at all (except for when condition is 0). Applying it to this situation, try adding state to your App component to utilize conditional rendering.
There's another key concept you need to understand: component props. They are essentially inputs to a component, certain properties passed to the component to tell how it should behave -- like attributes on regular HTML elements such as input placeholders, URLs, and event handlers. For example:
<Component foo="bar" bar={3} />
This will pass the props foo and bar down to Component with the values "bar" and 3 respectively and are accessible through this.props. If you were to access this.props.foo inside the Component component it would give you "bar". If you pair this up with composition, you can accomplish what you want:
class Example extends React.Component {
constructor() {
super();
this.state = {
showHello: true
}
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.setState(prevState => ({
showHello: !prevState.showHello
}));
}
render() {
return (
<div>
{this.state.showHello && <Child2 />}
This is a test.
<Child1 onClick={this.handleChange} />
</div>
);
}
}
class Child1 extends React.Component {
render() {
return <div onClick={this.props.onClick}>Click me!</div>
}
}
class Child2 extends React.Component {
render() {
return <div>Hello!</div>
}
}
ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
The above example lifts state up by having a parent compose the children and maintain the state. It then uses props to pass down an onClick handler to Child1, so that whenever Child1 is clicked, the state of the parent changes. Once the state of the parent changes, it will use conditional rendering to render <Child2> if the condition is truthy. Further reading at the React documentation and on the logical && operator.
I know how to set state in the component itself, but how do I affect another component when I click a different component?
The recommended way to do it would be to create a parent component that has the state. You'd then use that state to determine when to render the other child component.
I want to render BlackSpark when RedSpark is clicked, but I'm not sure how to change the state of a component in another component. Also, what if I want to hide BlackSpark when GreenSpark is clicked and GreenSpark is inside BlackSpark?
In this case, here's how you'd do it.
const GreenSpark = ({ onClick }) => (
<button className="green" onClick={onClick}>X</button>
)
const BlackSpark = ({ onClick }) => (
<div className="black">
<GreenSpark onClick={onClick} />
</div>
)
const RedSpark = ({ onClick }) => (
<div className="red" onClick={onClick}></div>
)
class Spark extends React.Component {
constructor(props) {
super(props)
this.state = {
showBlack: false
}
this.boundShowBlack = this.showBlack.bind(this)
this.boundHideBlack = this.hideBlack.bind(this)
}
showBlack() {
this.setState({ showBlack: true })
}
hideBlack() {
this.setState({ showBlack: false })
}
render() {
return (
<div>
<RedSpark onClick={this.boundShowBlack} />
{this.state.showBlack && <BlackSpark onClick={this.boundHideBlack} />}
</div>
)
}
}