如果用户登录或未登录,实现条件导航栏的策略

Posted

技术标签:

【中文标题】如果用户登录或未登录,实现条件导航栏的策略【英文标题】:Strategies to implementing a conditional Navbar if a user is logged in or not 【发布时间】:2020-08-08 10:33:05 【问题描述】:

背景:我正在创建一个非常基本的应用程序,它具有 4 种不同的功能:注册、登录、搜索和创建内容。截至目前,我可以登录用户并防止未经授权的用户进入受保护的路线。这在我的Protected.js 组件中如下所示。但是,我正在努力寻找一种方法来在位于我的App.js 的导航栏中有条件地显示登录/注销按钮

后续问题:我是否应该将身份验证逻辑从我的Login.js 路由中移到我的主要App.js 组件中并传递状态?那不是非常低效吗,因为每次我渲染一个新视图时,App.js 都会使用我的后端 API 执行另一个提取来验证用户是否有一个未过期的会话?这对某些人来说可能看起来微不足道,但我很难思考如何有效地有条件地显示登录/注销按钮。我已经研究过 Context API,所以如果你觉得有必要,我愿意接受一些建议,但我对 Redux 不熟悉(我一周前才开始 React)。我正在寻找经过深思熟虑的策略,因此如果您能尽力提供完整和连贯的回复,我将不胜感激。我真的很感激。

App.js

class App extends Component 
    render() 
        return(
            <Router>
                <div>
                    <Navbar bg="light" variant="light">
                        <Navbar.Brand href="#home">My Application</Navbar.Brand>
                        <Nav className="mr-auto">
                            <Link to="/signup" className="nav-link">Signup</Link>
                            <Link to="/login" className="nav-link">Login</Link>
                            <Link to="/feed" className="nav-link">Search</Link>
                            <Link to="/create-post" className="nav-link">Create</Link>
                        </Nav>
                        <Form inline>
                            <Button variant="outline-primary">Logout</Button>
                        </Form>
                    </Navbar>
                </div>
                <Switch>
                    <Route path = '/signup' component = Signup />
                    <Route path = '/login' component = Login />
                    <Route path = '/feed' component = Feed />
                    <ProtectedRoute path = '/create-post' component = CreatePost />
                </Switch>

            </Router>
        )
    

Login.js

 class Login extends Component 
    constructor(props) 
        super(props)

        this.state = 
            username: "",
            password: ""
        

        this.handleChange = this.handleChange.bind(this)
        this.handleSubmitForm = this.handleSubmitForm.bind(this)
    

    handleChange(event) 
        //console.log(event.target.name)
        this.setState([event.target.name]: event.target.value)
    

    handleSubmitForm() 
        const user = 
            username: this.state.username,
            password: this.state.password
        

        const url = 'http://localhost:9000/api/login'

        fetch(url, 
            method: 'POST',
            credentials: 'include',
            headers: 
                'Content-Type': 'application/json'
            ,
            body: JSON.stringify(user)
        )
        .then((response) => response.json())
        .then((data) => 
            if (data.status === 1) 
                this.props.history.push('/feed')
            
            console.log(data)
        )
        .catch((error) => 
            console.log('Error', error)
        )
    

    render() 
        return (
        <div>
            <Card className="card-login" style= width: '18rem' >
                <Card.Body>
                <Card.Title>Login</Card.Title>
                    <Form>
                        <Form.Group>
                            <Form.Control placeholder ="Username"
                                          name = "username"
                                          value = this.state.username
                                          onChange = this.handleChange/>
                        </Form.Group>
                        <Form.Group>
                            <Form.Control type ="password"
                                          placeholder="Password"
                                          name = "password"
                                          value = this.state.password
                                          onChange = this.handleChange />
                        </Form.Group>                   
                    <Button variant ="primary"
                            value="Submit"
                            onClick=this.handleSubmitForm>Submit
                    </Button>
                    </Form>
                </Card.Body>
            </Card>         
        </div>)
    

Protectedroute.js

class ProtectedRoute extends Component 
    constructor(props) 
        super(props)

        this.state = 
            isAuthenticated: false,
            isLoading: true
        
        this.isAuthenticated = this.isAuthenticated.bind(this)
    

    componentDidMount() 
        this.isAuthenticated()
    

    isAuthenticated() 
        const url = 'http://localhost:9000/api/auth'
        fetch(url, 
            method: 'GET',
            credentials: 'include',
            headers: 
                'Content-Type' : 'application/json'
            
        )
        .then((response) => response.text())
        .then((data) => 
            if (data === 'true') 
                this.setState(
                    isAuthenticated: true,
                    isLoading: false
                )
             else 
                this.setState(
                    isAuthenticated: false,
                    isLoading: false
                )
            
        )
        .catch((err) => 
            console.log('Error', err)
        )
    
    render() 
        const Component = this.props.component
        if (this.state.isLoading === true) 
            return (<div>Loading</div>)
        
        return(
            <Route render=(props) => this.state.isAuthenticated && !this.state.isLoading ? (<Component ...this.props/>) : (<Redirect to ='/login' />) />
        )
            
    

api.js(快速后端)

router.post('/login', function(req,res,next) 
    const query1 = "SELECT * FROM users WHERE username = TRIM(?) AND pass = TRIM(?)"
    database.query(query1, [req.body.username, req.body.password])
    .then((result) => 
        if (result.length) 
            if (req.session.username) 
                res.json('message': 'You are already logged in', 'status': 0)
             else 
                req.session.username = req.body.username
                res.json('message': 'You have successfully logged in', 'status': 1)
            
         else 
            if (req.session.username) 
                res.json('message': 'You are already logged in', 'status': 0)
             else 
                res.json('message': 'Incorrect Credentials', 'status': 0)
            
        
    ).catch((err) => res.send(err))
)



router.get('/auth', (req, res, next) => 
    if (req.session.username) 
        res.send(true)
     else 
        res.send(false)
    
)

【问题讨论】:

【参考方案1】:

嗯,显然需要全局状态,您可以使用 React Context、Redux 甚至在App 组件中设置身份验证状态,传递改变状态的方法,即。 setAuth,作为 Login 组件的道具。

class App extends Component 
  constructor(props) 
    super(props);
    this.setAuth = this.setAuth.bind(this);
    this.state = 
      isAuthenticated: false
    ;
  

  setAuth(value) 
    this.setState(
      isAuthenticated: value
    );
  
<Route path='/login' component=() => <Login setAuth=this.setAuth /> />

我是否应该将身份验证逻辑从我的 Login.js 路由中移出? 进入我的主要 App.js 组件并传递状态?那岂不是 效率非常低,因为每次我渲染一个新视图时, App.js 正在使用我的后端 api 执行另一个提取以验证是否 用户有一个未过期的会话?

您是指Protectedroute 组件中发生的逻辑吗?正如你所说,我会将其提取到App 组件中,因为在一个场景中

<ProtectedRoute path = '/create-post' component = CreatePost />
<ProtectedRoute path = '/post-list' component = PostList />
<ProtectedRoute path = '/update-post' component = UpdatePost />

当您想访问'/update-post' 部分时,它会检查用户是否经过多次身份验证。

【讨论】:

感谢您的详尽回答。您给出的第一个响应是在子组件中修改 App 状态的一个很好的示例。但是,关于您的第二个回复,我想确保我对您的理解正确:您是否建议我摆脱 ProtectedRoute 并将我所有的获取身份验证逻辑从 ProtectedRoute 移动到 App ?如果是这种情况,我的App 组件不会在每次安装新组件时执行获取吗? @Józef Podlecki 如果您将请求放入componentDidMount 然后更改状态,那么它仍然会再次执行获取。但是shouldComponentUpdate(nextProps) 来救援,在那里你可以创建一个非常简单的谓词,忽略isAuthenticated 属性更改或只返回false 很好。这是公认的答案。感谢您提供反馈。 @Józef Podlecki

以上是关于如果用户登录或未登录,实现条件导航栏的策略的主要内容,如果未能解决你的问题,请参考以下文章

用户在 Vue.js 和 Firebase 中注册后更改导航栏的状态

Rspec用于检查用户使用设计宝石登录或未登录的天气

用户登录时更改导航栏

组策略登录蝙蝠脚本

Laravel 5:如果用户登录,则更改导航栏

如何知道 Spotify 中的登录状态