Materialize modal is getting hijacked by React-Router

I’m trying to move the delete button’s functionality into a modal, so the user should get a confirmation before deleting from a single click. If I open the inspector I can manually make it appear by changing the display:None in CSS, but I thought that’s what the Materialize library was handling for me.

When I click on the modal, I see it appearing in the address bar, so I assume react-router is hijacking the modal

CaptureMODAL2.JPG

I can probably replace the exact path to match /modal2, but should I be sending it to a new component? Or send it back to the same component with a different prop set for modal?

#!jsx

import React from 'react';
import { Switch, Route} from 'react-router-dom';
import Barrels from './Barrels';
import About from './About';
import BarrelDetails from './BarrelDetails';
import AddBarrel from './AddBarrel';
import BarrelEdit from './BarrelEdit';

const Main = () => (
    <main className="green">
    <Switch>
    <Route exact path= '/' component={Barrels} />
    <Route exact path= '/about' component={About} />
    <Route exact path= '/barrels/add' component={AddBarrel} />
    <Route exact path= '/barrels/edit/:id' component={BarrelEdit} />
<Route exact path= '#modal2' component={BarrelEdit     //modal component propperty turned on?//               } />
    <Route exact path= '/barrels/:id' component={BarrelDetails} />
    </Switch>
    </main>
    )

export default Main;

live demo on Heroku

Repository on BitBucket
or should I be trying to move the modal trigger into the on onDelete function?

#!jsx

import React, { Component } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
import logo from '../logo.svg';
class BarrelDetails extends Component {
    constructor (props){
       super(props);
       this.state = {
        details: ''
    }
}

componentWillMount(){
    this.getBarrel();    
}

getBarrel(){
    let barrelID = this.props.match.params.id;
    axios.get(`/api/Barrels/${barrelID}`)
    .then (response => {
      this.setState({details: response.data}, () =>
      {
        console.log(this.state);
      })
    })
    .catch(err => console.log(err));
  }

onDelete(){
    let barrelID = this.state.details.id;
    axios.delete(`/api/Barrels/${barrelID}`)
    .then ( response => {
        this.props.history.push('/');
    } ).catch(err => console.log(err));
}

render () {
    return (
        <div className = "container" >

        <header className="App-header z-depth-3">
        <h2>{this.state.details.Name}</h2>
        <Link className = "btn grey" to = "/">back</Link>
        </header>

            <ul className = "collection z-depth-3" >
            <li className = "collection-item" >planted: <b className = "yellow" > {this.state.details.date_planted}</b> </li>
            <li className = "collection-item" >Barrel #: <b className = "yellow" > {this.state.details.barrel_number}</b> </li>
            <li className = "collection-item" ><b className = "yellow" > {this.state.details.contents}</b> </li>
            <li className = "collection-item" >location: <b className = "yellow" > {this.state.details.location}</b> </li>
            <li className = "collection-item" >geolocation: <b className = "yellow" > this.state.details.geoLocaction.toString()</b> </li>
            <li className = "collection-item" >notes: <b className = "yellow" > {this.state.details.notes}</b> </li>
            <li className = "collection-item" >size: <b className = "yellow" > {this.state.details.size}</b> </li>
            <li className = "collection-item" >last checked: <b className = "yellow" > {this.state.details.date_last_checked}</b> </li>
            </ul>
            <button onClick = {this.onDelete.bind(this) } className = "btn red right"><i className ="far fa-trash-alt"></i> Delete this Barrel</button>

        <h5>what that modal do?</h5>

        <Link to={`/barrels/edit/${this.state.details.id}`} className="btn waves-effect z-depth-3"><i className = "fas fa-pencil-alt" ></i> Edit this Barrel</Link>
        <Link to={`#modal2`} className="btn waves-effect red"><i className ="far fa-trash-alt z-depth-3"></i> Delete this Barrel</Link>

        
        <div id="modal1" className="modal">
          <div className="modal-content">
            <h4>Modal Header</h4>
            <p>A bunch of text</p>
          </div>
          <div className="modal-footer">
            <a href="" className="modal-action modal-close waves-effect waves-green">Button</a>
          </div>
        </div>

        <div id="modal2" className="modal orange">
          <div className="modal-content">
            <h4>Are you sure you want to delete</h4>
            <p>A bunch of text</p>
          </div>
          <div className="modal-footer">
            <a href="" className="modal-action modal-close waves-effect waves-green">Button</a>
          </div>
        </div>
            
            
            
        <p className="App-intro">
        TurtleWolfe.com<br/>
        using LoopBack & React<br/>
        <img src={logo} className="App-logo" alt="logo" />
        </p>
            
            </div>
           )
}
}

export default BarrelDetails;

1 Like
<Link to={`#modal2`} className="btn waves-effect red">
    <i className ="far fa-trash-alt z-depth-3"></i> 
    Delete this Barrel
</Link>

This is the part that concerns me. Why are you trying to handle the modal via the route? This should not be a <Link> as that is attached to React-Router, you should just be using a button with the styling turned off, then triggering the modal via an onClick event.

You’re also trying to show your modal in a way that probably won’t work with React. You’re going to want to set a local state of dislpayModal: false or something like that, then do a check for that state in your render, instead of relying on Materalize to do it for you. It can be tricky to get DOM based plugins to work in a React environment.

Have you tried this? https://react-materialize.github.io

Also the use of backticks for non-templated literals is not suggested. Use ' single quotes.

If you’re trying to learn React, feel free to tag me on any questions you have.

2 Likes

If you have a github account, you can claim credit for your answer.

Thanks, I have an SO account that I rarely use. But I don’t think I actually answered anything, did I? At least not in a way that would be acceptable to SO standards.

At least not in a way that would be acceptable to SO standards.

I disagree man. You answered the question spot on and gave the OP some suggestions for improving their code. That’s certainly acceptable.

2 Likes

I’m probably not the best judge of their standards, but you really helped me out… I was definitely off the rails and barking up the wrong tree. and everyone let it sit there

1 Like

@mawburn I think my next step would be to add a login component. I know on the back end, I’d be getting an access token from the loopback API, but on the front end I need to add a login component to the nav bar and then wire that into ACL… are there any tutorials you’d recommend to cover that territory?

I don’t know of any tutorials, sorry.

You could just return a plaintext role list to optionally display Navbar links, then keep that in the global store, then use an auth helper in the main router file or on the containers to kick un-authenticated users out of places they shouldn’t be. That’s what I’ve always done. I’ve never put a whole lot of thought into making it rock solid, because it’s a frontend app and as long as the user can’t actually pull data they aren’t allowed to see from the backend, then it should be fine.

1 Like

You’re looking for a tutorial on how to create protected routes in React? Did I get that right?

1 Like

probably… I think I’ve got the general concept. I’m probably overthinking it, but yeah… do you have one? mainly they should be logged in to delete their own entries, can only delete their own entries but can see everyone’s on the home page even if they don’t have an account. and specifically with Loopback, in case there are minor differences with Express, even though it’s built on it.

We have an article on React Router:

If you check out the Protecting Routes section, as the name suggests, that explains how to create a protected route. The general theory is that you create a <PrivateRoute> component to which you pass an authed prop, as well as a ‘protected’ component. If the user is authed, the protected component is rendered, otherwise the user is redirected to a login component (for example).

Implementing the method which returns the authed state is left to you (i.e. not covered in the article). One way to do this would be using JWTs, which you can store on the client and pass along to each request to a protected route.

Here’s how you might do that with Loopback:

https://dzone.com/articles/developing-restful-apis-with-loopback

2 Likes

yes, both those articles help… I hope to get through the Chuck Norris tutorial it links to. It looks to cover it in detail

looks like Autho0 has a known security flaw, I’m not sure if it was hashed or not…

I’m looking into what he’s doing here with FireBase… I thought I could just get the token from LoopBack, but I guess that’s just log in, not authentication. It’s a bit overwhelming…

I’m not very familiar with Loopback, sorry. I recently implemented a similar setup with Rails, so can advise on the general concepts, but not the specific technology.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.