I'm trying to append a Modal component to the doc body on click of a trigger, but I think my syntax is wrong...
openModal() {
console.log(this.state.isModalOpen);
this.setState({ isModalOpen: true });
$('body').append(
<Modal isOpen={this.state.isModalOpen} transitionName="modal-anim">
<NewAlbum onClick={this.closeModal}/>
</Modal>
)
}
<button onClick={this.openModal}>Trigger</button>
Yeah you have missed a few of the React fundamentals. You need to let React take care of your rendering for you. This is the typical usage for what you are after:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { isModalOpen: false };
this.toggleModal = this.toggleModal.bind(this);
}
toggleModal() {
this.setState({ isModalOpen: !this.state.isModalOpen });
}
render() {
return (
<div>
<button onClick={this.toggleModal}>Trigger</button>
<Modal isOpen={this.state.isModalOpen} transitionName="modal-anim">
<NewAlbum onClick={this.toggleModal}/>
</Modal>
</div>
)
}
}
I would highly recommend you go through the react documentation and get some of the foundations cemented: https://facebook.github.io/react/docs/tutorial.html
Related
So I am quite new to React world, and I have this problem I am trying to solve, but I don't quite understand why it is happening.
So I want to pass the state of component to parent component and from parent component to child component and everything look okay, and in console log the state goes trough, but nothing changes. I believe there is a way I need to listen for state change or something within child component so it works. If I put true in the parent component, child component also get's true, but if I toggle it on click, it goes trough but nothing changes in the child component.
Also I understand my code is little rough right now ill reafactor it later, but right now I am trying to understand why it does not work.
If anyone could help me I would be thankful for it.
This is component that controls the state.. So the state passes from TurnOnBtn to App and from App it goes to TodoList
import "./Todo.css";
class TurnOnBtn extends Component {
constructor(props) {
super(props);
this.state = { display: false };
this.handleState = this.handleState.bind(this);
}
handleState() {
this.setState({ display: !this.state.display });
this.props.checkDisplay(this.state.display);
}
render() {
return (
<button onClick={this.handleState} className="TurnOnBtn">
<i className="fa fa-power-off"></i>
</button>
);
}
}
export default TurnOnBtn;
parent component App
import TurnOnBtn from "./TurnOnBtn";
import TheMatrix from "./TheMatrxHasYou";
import TodoList from "./TodoList";
import { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = { display: true };
this.checkDisplay = this.checkDisplay.bind(this);
}
checkDisplay(newDisplay) {
this.setState({
display: newDisplay,
});
console.log(this.state);
}
render() {
return (
<div className="App">
<TodoList display={this.state.display} />
<TheMatrix />
<TurnOnBtn checkDisplay={this.checkDisplay} />
</div>
);
}
}
export default App;
child component TodoList
import Todo from "./Todo";
import NewTodoForm from "./NewTodoForm";
import { v4 as uuid } from "uuid";
import "./Todo.css";
class TodoList extends Component {
constructor(props) {
super(props);
this.state = {
todos: [],
displayOn: this.props.display,
};
this.newTodo = this.newTodo.bind(this);
this.editTodo = this.editTodo.bind(this);
this.deleteTodo = this.deleteTodo.bind(this);
}
editTodo(id, updatedTask) {
const updatedTodo = this.state.todos.map((todo) => {
if (todo.id === id) {
return { ...todo, todo: updatedTask };
}
return todo;
});
this.setState({
todos: updatedTodo,
});
console.log(updatedTask);
}
deleteTodo(id) {
this.setState({
todos: this.state.todos.filter((todo) => todo.id !== id),
});
}
newTodo(newState) {
this.setState({
todos: [...this.state.todos, { ...newState }],
});
}
render() {
return (
<div
style={this.state.displayOn ? { opacity: 1 } : { opacity: 0 }}
className="Todo-screen"
>
{" "}
<div className="TodoList">
<div className="TodoList-todos">
{" "}
{this.state.todos.map((todo) => (
<Todo
key={uuid()}
id={todo.id}
active={todo.active}
editTodo={this.editTodo}
deleteTodo={this.deleteTodo}
todoItem={todo.todo}
/>
))}
</div>
</div>{" "}
<NewTodoForm newTodo={this.newTodo} />
</div>
);
}
}
export default TodoList;
The bug here is in these line of codes:
handleState() {
this.setState({ display: !this.state.display });
this.props.checkDisplay(this.state.display);
}
Remember setState is an async function, so by the time you set a new state using setState, the value for this.state is not guaranteed changed.
One way to fix this is using the setState callback, which will run after the state is changed:
handleState() {
this.setState({ display: !this.state.display }, function() {
this.props.checkDisplay(this.state.display);
});
}
But you don't need to use another state to keep display state in TurnOnBtn as you can pass the toggle callback from the parent:
App.js
class App extends Component {
constructor(props) {
super(props);
this.state = { display: true };
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState({
display: !this.state.display,
});
}
render() {
return (
<div className="App">
<TodoList display={this.state.display} />
<TheMatrix />
<TurnOnBtn toggleDisplay={this.toggleDisplay} />
</div>
);
}
}
TurnOnBtn.js
class TurnOnBtn extends Component {
constructor(props) {
super(props);
this.handleState = this.handleState.bind(this);
}
handleState() {
this.props.toggleDisplay();
}
render() {
return (
<button onClick={this.handleState} className="TurnOnBtn">
<i className="fa fa-power-off"></i>
</button>
);
}
}
I'm trying to call child component from parent component in reactjs using refs.but it throws error saying showModal() is not a function when I tried to call.
//app.js
class app extends Component {
constructor(props) {
super(props);
this.POPUP = React.createRef();
}
showModal(){
this.POPUP.showModal(true);
}
render() {
return (
<React.Fragment>
<span><a onClick={() => this.showModal()}>Show</a></span>
<POPUP onRef={ref => (this.POPUP = ref)}></POPUP>
</React.Fragment >
)
}
}
popup.js
class POPUP extends Component {
showModal(show) {
console.log('showmodal');
}
render() {
console.log(this.props.showModalPopup);
<React.Fragment>
<Modal
position="center">
<div>
//code
</div>
</Modal>
</React.Fragment>
)
}
}
Is there any alternative in nextjs.please help
https://reactjs.org/docs/refs-and-the-dom.html#accessing-refs
First of all if you want to access that POPUP instance you should do
this.POPUP.current.showModal(true);
BTW Your showModal function needs to be bound to the child component if you intend to alter its state.
However, even this is doable - this is usually not the recommended way of doing React.
If you want the parent to decide if showModalPopup should be true, you probably should keep the state inside of your parent component:
class App extends Component {
constructor(props) {
super(props);
this.state = { showModalPopup: false };
this.showModal = this.showModal.bind(this);
}
showModal(){
this.setState({ showModalPopup: true });
}
render() {
return (
<React.Fragment>
<span><a onClick={this.showModal}>Show</a></span>
<POPUP show={this.state.showModalPopup}></POPUP>
</React.Fragment >
)
}
}
const POPUP = ({ show }) => (
<Modal show={show} position="center">
// your content.
</Modal>
)
I have a Modal component in the Main.js app and I want to trigger it from a different component (in this case Homepage, but I have one component for each page).
I don´t know how to pass a component to be rendered inside the modal.
If it helps I´m using Context API.
App.js
const App = () => {
return (
<div>this is the main app</div>
<Modal />
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Modal.js
class Modal extends Component {
constructor(props) {
super(props)
this.state = {
'open': false
}
}
componentWillReceiveProps(nextProps) {
this.setState({open: nextProps.open})
}
render() {
if (!this.state.open) {
return false
}
return(
<div className="modal">
{this.props.children}
</div>
)
}
}
export default Modal
Homepage.js
class Homepage extends Component {
constructor(props) {
super(props)
this.handleOpenModal = this.handleOpenModal.bind(this)
}
handleOpenModal() {
// here I want to open the modal and pass the <ModalContent /> component
// basically call the <Modal open="true"> from the Main component
}
render() {
return(
<div className="homepage">
<button onClick={this.handleOpenModal}>open the modal</button>
</div>
)
}
}
const ModalContent = () => {
return(
<div>this is the content I want to render inside the modal</div>
)
}
thank you.
I strongly recommend using something like react-modal (npm). It allows you to keep modal content right next to the trigger. It does this by appending a "portal" high up in the DOM and handles appending the content to is.
Your example may look like the following:
import Modal from 'react-modal';
class Homepage extends Component {
constructor(props) {
super(props)
this.state = { modalOpen: false };
this.handleOpenModal = this.handleOpenModal.bind(this)
}
handleOpenModal() {
this.setState({ modalOpen: true });
}
render() {
return(
<div className="homepage">
<button onClick={this.handleOpenModal}>open the modal</button>
<Modal open={this.state.modalOpen}>
<ModalContent />
</Modal>
</div>
)
}
}
const ModalContent = () => {
return(
<div>this is the content I want to render inside the modal</div>
)
}
The popup show up after 1 sec. But I need to show it only to user who doesn't subscribe yet, by using localStorage. I did try use local storage like this code below, but then all I've got is a blank white page when it's time to show/not show popup. Is the localStorage I coded was totally wrong? Please help
import React from 'react'
import styled from 'react-emotion'
import Rodal from 'rodal'
import '../styles/rodal.css'
import Delayed from '../components/Delayed'
const Signup = () => (
<Containers>
<SubsribtionForm
action="https://subscribe/post?/....."
method="post"
>
<SubscribeInput type="email" name="EMAIL" placeholder="Subscribe to Updates!" required />
<Button type="submit" onClick={this.submit}>Add Me</Button>
</SubsribtionForm>
</Containers>
)
export default class Popup extends React.Component {
constructor(props) {
super(props)
this.state = { visible: true }
if (localStorage.getItem('submit')) {
this.setState({ visible: false })
}
this.submits = this.submits.bind(this)
}
submits() {
const newsub = this.state.submit
localStorage.setItem('submit', newsub)
}
show() {
this.setState({ visible: true })
}
hide() {
this.setState({ visible: false })
}
render() {
return (
<div>
<Container>
<Delayed waitBeforeShow={1000}>
<Rodal
visible={this.state.visible}
onClose={this.hide.bind(this)}
width={500}
height="100%"
customStyles={customStyles}
>
<Box>
<Banner />
<ContainerContent>
<Header>Subscribe to our mailing list</Header>
<Words>
We will organize and send regular updates Stay informed!
</Words>
</ContainerContent>
<ContainerForm>
<Signup />
</ContainerForm>
</Box>
</Rodal>
</Delayed>
</Container>
</div>
)
}
}
constructor(props) {
super(props)
this.state = {
visible: !(localStorage.getItem('submit') !== '' && localStorage.getItem('submit') !== null),
}
this.submits = this.submits.bind(this)
}
Just check if submit is not empty, like above.
Another approach would be to do the following in componentDidMount life cycle
componentDidMount() {
if (localStorage.getItem('submit')) {
this.setState({ visible: false })
}
}
You are calling this.setState inside the class constructor, you can use a simple conditional in this.state to assign the value directly and please use the bind in the constructor :D. I use the length because if the string is '' the length is 0 then that value in the conditional is false
import React from 'react'
import styled from 'react-emotion'
import Rodal from 'rodal'
import '../styles/rodal.css'
import Delayed from '../components/Delayed'
const Signup = () => (
<Containers>
<SubsribtionForm
action="https://subscribe/post?/....."
method="post"
>
<SubscribeInput type="email" name="EMAIL" placeholder="Subscribe to Updates!" required />
<Button type="submit" onClick={this.submit}>Add Me</Button>
</SubsribtionForm>
</Containers>
)
export default class Popup extends React.Component {
constructor(props) {
super(props)
const submit = localStorage.getItem('submit')
this.state = { visible: !submit && !submit.length }
this.submits = this.submits.bind(this)
this.show = this.show.bind(this)
this.hide = this.hide.bind(this)
}
submits() {
const newsub = this.state.submit
localStorage.setItem('submit', newsub)
}
show() {
this.setState({ visible: true })
}
hide() {
this.setState({ visible: false })
}
render() {
return (
<div>
<Container>
<Delayed waitBeforeShow={1000}>
<Rodal
visible={this.state.visible}
onClose={this.hide}
width={500}
height="100%"
customStyles={customStyles}
>
<Box>
<Banner />
<ContainerContent>
<Header>Subscribe to our mailing list</Header>
<Words>
We will organize and send regular updates Stay informed!
</Words>
</ContainerContent>
<ContainerForm>
<Signup />
</ContainerForm>
</Box>
</Rodal>
</Delayed>
</Container>
</div>
)
}
}
I'm trying to create a MessageBox(Modal Box) element which gets inputs to form the Modal Box, The close method in MessageBox somehow doesn't call the parent close and inturn get the Modal disappear, any help please ??
export default class MessageBox extends Component{
constructor(props) {
super(props);
this.close = this.close.bind(this);
}
close(){
this.props.callbackParent;
}
render(){
return (
<div>
<Modal show={this.props.visibility} onHide={this.close}>
<ModalHeader closeButton>
<ModalTitle>{this.props.msgHeader}</ModalTitle>
</ModalHeader>
<ModalBody>{this.props.msgBody}</ModalBody>
</Modal>
</div>
);
}
}
export default class Product extends Component {
constructor(props) {
super(props);
this._initState = {
showModal: false
}
this.state = this._initState;
this.open = this.open.bind(this);
this.close = this.close.bind(this);
}
open(){
this.setState(
{ showModal: true }
);
}
close(){
this.setState(
{ showModal: false }
);
}
render(){
return (
<div>
<Button bsStyle="primary" bsSize="large" onClick={this.open}>
Go!!!
</Button>
<MessageBox visibility={this.state.showModal} msgHeader='Header goes here ...' msgBody='Overlay message body goes here ..' callbackParent={this.close}/>
</div>
);
}
};
It seems you are missing the parenthesis on in the close method of MessageBox.
this.props.callbackParent;
should be
this.props.callbackParent();
(thx to #azium) for the answer.
I got it working by this
export default class MessageBox extends Component{
constructor(props) {
super(props);
}
render(){
return (
<div>
<Modal show={this.props.visibility} onHide={this.props.callbackParent}>
<ModalHeader closeButton>
<ModalTitle>{this.props.msgHeader}</ModalTitle>
</ModalHeader>
<ModalBody>{this.props.msgBody}</ModalBody>
<ModalFooter>
<Button onClick={this.props.callbackParent}>Close</Button>
</ModalFooter>
</Modal>
</div>
);
}
}