React Redux Connect: необученное инвариантное нарушение: не удалось найти «хранилище» - PullRequest
0 голосов
/ 07 января 2020

Привет! Я работаю над React Redux application, реализуя persistStore для сохранения моих состояний при перезагрузке страницы.

Я успешно реализовал механизм на моих 1st component и использовал тот же подход на моем 2nd component. Тем не менее, я получаю сообщение об ошибке на втором компоненте, и я не уверен, в чем проблема, поскольку я просто скопировал то, что сделал в первом компоненте: msgstr "в контексте" Connect (NewsDetail) ". Либо оберните компонент root в , либо передайте пользовательский поставщик контекста React в и соответствующего потребителя контекста React для Connect (NewsDetail) в параметрах соединения.

Дон не понимаю, поскольку я уже завернул основной компонент в <Provider>.

// Это мой второй компонент

NewsDetailApp. js (main компонент)

import React, { Component } from 'react';
import { BrowserRouter, Route, Redirect, Switch} from 'react-router-dom'
import logo from '../../../logo.svg';
import '../../../App.css';

import {Provider} from 'react-redux';
import store from '../../../store';
import {persistStore} from 'redux-persist';
import {PersistGate} from 'redux-persist/es/integration/react'

import NewsDetail from './components/NewsDetail';

class NewsDetailApp extends Component {
  render() {
      const  persistedStore = persistStore(store)
    return (
        <Provider store={store}>
          <PersistGate persistor={persistedStore} loading={null}>
          <BrowserRouter>
            <Switch>
              <Route component={NewsDetail}/>
            </Switch>
          </BrowserRouter>
        </PersistGate>
      </Provider>
    );
  }
}

export default NewsDetailApp;

NewsDetail. js компонент

import React, {Component} from 'react'
import 'whatwg-fetch'
import cookie from 'react-cookies'
import { Link } from 'react-router-dom'

import PropTypes from 'prop-types';
import {connect} from 'react-redux';

import moment from 'moment'
import parse from 'html-react-parser';

// import PostCommentApp from '../../comment/PostCommentApp'
import PostCommentForm from '../../comment/components/PostCommentForm'

class NewsDetail extends Component {
    constructor(props){
        super(props)
        this.state = {
             slug: null,
             news: null,
             doneLoading: false,
        }
    }

    loadNews(slug){
      const endpoint = `/api/posts/${slug}/`
      let thisComp = this
      let lookupOptions = {
          method: "GET",
          headers: {
              'Content-Type': 'application/json'
          }
      }

      const csrfToken = cookie.load('csrftoken')
      if (csrfToken !== undefined) {
          lookupOptions['credentials'] = 'include'
          lookupOptions['headers']['X-CSRFToken'] = csrfToken
       }

      fetch(endpoint, lookupOptions)
      .then(function(response){
          if (response.status == 404){
              console.log('Page not found')
          }
          return response.json()
      }).then(function(responseData){
          if (responseData.detail){
              thisComp.setState({
                  doneLoading: true,
                  news: null
              })
          } else {
           thisComp.setState({
                  doneLoading: true,
                  news: responseData
              })
          }
      }).catch(function(error){
          console.log("error", error)
      })
  }
    componentDidMount(){
        this.setState({
                slug: null,
                news: null
            })
        if (this.props.match){
            const {slug} = this.props.match.params
            this.setState({
                slug: slug,
                doneLoading: false
            })
            this.loadNews(slug)
            window.scrollTo(0, 0)
        }
    }
    render(){
        const {doneLoading} = this.state
        const {news} = this.state
        return(
        <React.Fragment>{(doneLoading === true) ? <div>
          {news === null ? <div class="col-lg-11">
            <div class="row">
              <div class="article">
                <p class="lead"><small>Sorry, this article could not be found. It may have been moved to another location, or deleted permanently.</small></p>
              </div>
              </div>
            </div>
            :
            <div class="row">
              <div class="col-md-1 d-none d-none">
                <div class="card border-0">
                  <ul class="list-group list-group-flush text-center">
                    <a href={'https://www.facebook.com/sharer/sharer.php?u='+window.location.href} target="_new"><li class="list-group-item rounded-0 bg-white"><i class="fab fa-facebook-square text-primary h3"></i></li></a>
                    <a href={'https://twitter.com/intent/tweet?url='+window.location.href} target="_new"><li class="list-group-item rounded-0 bg-white"><i class="fab fa-twitter text-info h3"></i></li></a>
                  </ul>
                </div>
              </div>
              <div class="col-lg-11">
                <div class="article">
                  <div class="article-heading">
                    <h3 class="font-weight-bolder">{news.title}</h3>
                    <p class="text-danger mb-0"><small class="font-weight-bolder">{news.author.first_name + ' ' + news.author.last_name}</small></p>
                    <p class="mb-0 text-muted"><small><i>{moment(news.publish_date).format('MMM D, YYYY hh:mm A')}</i></small></p>
                    <ul class="list-group list-group-horizontal mt-3 mb-3">
                      <a href={'https://www.facebook.com/sharer/sharer.php?u='+window.location.href} target="_new"><li class="list-group-item p-0 pr-3 border-0 rounded-0 bg-white"><i class="fab fa-facebook-square text-primary h5"></i></li></a>
                      <a href={'https://twitter.com/intent/tweet?url='+window.location.href} target="_new"><li class="list-group-item p-0 pr-3 border-0 rounded-0 bg-white"><i class="fab fa-twitter text-info h5"></i></li></a>
                    </ul>
                  </div>
                  <div class="article-image">
                    <img class="w-100" src={news.feature_image}/>
                    <div class="bg-light p-2">
                      <p class="text-muted mb-0"><small>{news.feature_image_caption}</small></p>
                    </div>
                  </div>
                  <div class="article-content mt-2">
                    <p class="">{parse(news.content)}</p>
                  </div>
                </div>
                <hr/>
                <PostCommentForm post={news.id} user={this.props.user}/>
              </div>
            </div>
          }
          </div> : "Loading..."}</React.Fragment>
        )
    }
}

NewsDetail.propTypes = {
  user: PropTypes.array.isRequired
}

const mapStateToProps = state => ({
  user: state.user.user
});

export default connect(mapStateToProps)(NewsDetail);

// Это мой 1-й компонент, где он работает нормально

RegistrationApp. js (основной компонент)

import React, { Component } from 'react';
import { BrowserRouter, Route, Redirect, Switch} from 'react-router-dom'
import logo from '../../../logo.svg';
import '../../../App.css';

import {Provider} from 'react-redux';
import store from '../../../store';
import {persistStore} from 'redux-persist';
import {PersistGate} from 'redux-persist/es/integration/react'

import Registration from './components/Registration';

class RegistrationApp extends Component {
  render() {
      const  persistedStore = persistStore(store)
    return (
        <Provider store={store}>
          <PersistGate persistor={persistedStore} loading={null}>
          <BrowserRouter>
            <Switch>
              <Route component={Registration}/>
            </Switch>
          </BrowserRouter>
        </PersistGate>
      </Provider>
    );
  }
}

export default RegistrationApp;

Регистрация. js компонент

import React, {Component} from 'react'
import 'whatwg-fetch'
import cookie from 'react-cookies'
import moment from 'moment'

import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {authLogin, authLogout} from '../../../../actions/authActions'

class Registration extends Component {
    constructor(props){
        super(props)
        this.handleLogin = this.handleLogin.bind(this)
        // this.handleSignup = this.handleSignup.bind(this)
        this.handleLogout = this.handleLogout.bind(this)
        // this.handleSingupFieldChange = this.handleSingupFieldChange.bind(this)
        this.validateEmailFormat = this.validateEmailFormat.bind(this)
        this.updateUsernameValue = this.updateUsernameValue.bind(this)
        this.updatePasswordValue = this.updatePasswordValue.bind(this)

        this.usernameLoginRef = React.createRef()
        this.passwordLoginRef = React.createRef()

        this.emailSignupRef = React.createRef()
        this.usernameSignupRef = React.createRef()
        this.passwordSignupRef = React.createRef()
        this.passwordConfirmSignupRef = React.createRef()

        this.state = {
          emailSignup: '',
          usernameSignup: '',
          password1Signup: '',
          password2Signup: '',

          usernameLogin: '',
          passwordLogin: '',
          token: sessionStorage.getItem('user_token'),
          loginError: '',
          isAuthenticated: sessionStorage.getItem('user_isauthenticated'),

          emailSignupError: "",
          usernameSignupError: "",
          passwordSignupError: "",
          passwordConfirmSignupError: "",
          isExistingUser: false
        }
    }

    // getToken(data){
    //   const endpoint = `/account/auth/login/`
    //   let thisComp = this
    //     let lookupOptions = {
    //       method: "POST",
    //       headers: {
    //         'Content-Type': 'application/json',
    //       },
    //       body: JSON.stringify(data),
    //       credentials: 'include'
    //     }

    //     fetch(endpoint, lookupOptions)
    //     .then(function(response){
    //       return response.json()
    //     }).then(function(responseData){
    //       if(responseData.token === undefined || responseData.token === null || responseData.token == null){
    //         thisComp.setState({
    //           loginError: responseData.non_field_errors
    //         })
    //         sessionStorage.setItem('user_token', null)
    //         sessionStorage.setItem('user_isauthenticated', false)
    //       }else {
    //         thisComp.setState({
    //           token: responseData.token
    //         })
    //         sessionStorage.setItem('user_token', responseData.token)
    //         sessionStorage.setItem('user_isauthenticated', true)
    //         thisComp.setState({
    //           isAuthenticated: sessionStorage.getItem('user_isauthenticated')
    //         })
    //         window.location.href = "/applicant/dashboard/"
    //       }
    //     })
    // }

    // getExistingUsername(username){
    //   const endpoint = `/api/users/${username}/`
    //   let thisComp = this
    //   let lookupOptions = {
    //       method: "GET",
    //       headers: {
    //           'Content-Type': 'application/json'
    //       }
    //   }

    //   const csrfToken = cookie.load('csrftoken')
    //   if (csrfToken !== undefined) {
    //       lookupOptions['credentials'] = 'include'
    //       lookupOptions['headers']['X-CSRFToken'] = csrfToken
    //    }

    //   fetch(endpoint, lookupOptions)
    //   .then(function(response){
    //       if (response.status == 404){
    //           console.log('Page not found')
    //       }
    //       return response.json()
    //   }).then(function(responseData){
    //       if (responseData.length>-1){
    //           thisComp.setState({
    //               isExistingUser: false,
    //               usernameSignupError: ''
    //           })
    //       } else {
    //        thisComp.setState({
    //               isExistingUser: true,
    //               usernameError: 'Username already exists.'
    //           })
    //       }
    //   }).catch(function(error){
    //       console.log("error", error)
    //   })
    // }

    // handleSingupFieldChange(event){
    //   var targetname = event.target.name
    //   this.setState({
    //     [targetname]:event.target.value
    //   }, () => {
    //     if(targetname=="password1Signup"||targetname=="password2Signup"){
    //       this.validateMatchingPassword()
    //     }
    //   })
    // }

    handleLogin(event){
      event.preventDefault()
      // this.getToken({username:this.state.usernameLogin, password:this.state.passwordLogin})
      this.props.authLogin(this.state.usernameLogin, this.state.passwordLogin);
    }

    // handleSignup(event){
    //   event.preventDefault()
    //   // this.getExistingUsername('superadmin')

    //     const endpoint = `/register/`
    //     let thisComp = this
    //       let lookupOptions = {
    //         method: "POST",
    //         headers: {
    //           'Content-Type': 'application/json',
    //         },
    //         body: JSON.stringify({email:this.state.emailSignup, username:this.state.usernameSignup, password:this.state.password1}),
    //         credentials: 'include'
    //       }

    //       fetch(endpoint, lookupOptions)
    //       .then(function(response){
    //         return response.json()
    //       }).then(function(responseData){
    //         if(responseData.auth_token === undefined || responseData.auth_token === null || responseData.auth_token == null){
    //           thisComp.setState({
    //             loginError: responseData.non_field_errors
    //           })
    //           sessionStorage.setItem('user_token', null)
    //           sessionStorage.setItem('user_isauthenticated', false)
    //         }else {
    //           thisComp.setState({
    //             token: responseData.auth_token
    //           })
    //           sessionStorage.setItem('user_token', responseData.auth_token)
    //           sessionStorage.setItem('user_isauthenticated', true)
    //           thisComp.setState({
    //             isAuthenticated: sessionStorage.getItem('user_isauthenticated')
    //           })
    //           window.location.href = "/applicant/dashboard/"
    //         }
    //       })
    // }

    handleLogout(event){
      // event.preventDefault()
      // sessionStorage.removeItem('user_token')
      // sessionStorage.setItem('user_isauthenticated', false)
      // window.location.href = "/"
      this.props.authLogout(this.props.user.token);
    }

    updateUsernameValue(event) {
        this.setState({
            usernameLogin: event.target.value
        })
    }

    updatePasswordValue(event) {
        this.setState({
            passwordLogin: event.target.value
        })
    }

    validateEmailFormat(){
      if(!this.state.emailSignup.includes('#')){
        this.setState({emailSignupError: 'The email address entered has invalid format. Correct format includes @ followed by domain (e.g. @gmail.com)'})
      }else{
        this.setState({emailSignupError: ''})
      }
    }

    validateMatchingPassword(){
      if(this.state.password1Signup.length>0){
        if(this.state.password1Signup.length<8 || this.state.password2Signup.length>32){
          this.setState({passwordSignupError: 'password must be 8-32 characters long'})
        }else{
          this.setState({passwordSignupError: ''})
          if(this.state.password1Signup!=this.state.password2Signup){
            this.setState({passwordConfirmSignupError: 'passwords do not match'})
          }else{
            this.setState({passwordConfirmSignupError: ''})
          }
        }
      }
    }

    // componentDidMount(){
    //   this.setState({
    //     token: sessionStorage.getItem('user_token'),
    //     isAuthenticated: sessionStorage.getItem('user_isauthenticated'),
    //   })
    // }

    render(){
        // const {token} = this.state
        // const {isAuthenticated} = this.state
        const user = this.props.user
        return (
          <div>
          {user.authenticated ? <ul class="nav navbar-nav navbar-right ml-auto">
                <li class="nav-item dropdown">
                  <a data-toggle="dropdown" class="nav-link dropdown-toggle text-light" href="#">
                                <span class="text-light"><i class="fa fa-user text-light"></i> {user.user.username}</span>
                        </a>
                  <ul class="dropdown-menu">
                            <li><a href="{% url 'jobnet_app:applicant_account' %}" class="dropdown-item"><i class="fa fa-user"></i> Account </a></li>
                            <li><a class="dropdown-item" href="javascript:void()" onClick={this.handleLogout}><i class="fa fa-sign-out"></i> Logout</a></li>
                  </ul>
                </li>
                </ul>
          : <ul class="nav navbar-nav navbar-right ml-auto form-login-signup">
            <li class="nav-item mr-3 mt-1">
              <a data-toggle="dropdown" class="nav-link dropdown-toggle text-light" href="#">Login</a>
              <ul class="dropdown-menu form-wrapper">
                <li>
                <form onSubmit={this.handleLogin}>
                  <div class="form-group">
                    <input
                      type="text"
                      class="form-control"
                      placeholder="Username"
                      required="required"
                      name="usernameLogin"
                      ref={this.usernameLoginRef}
                      onChange={this.updateUsernameValue}/>
                  </div>
                  <div class="form-group">
                    <input
                      type="password"
                      class="form-control"
                      placeholder="Password"
                      required="required"
                      name="passwordLogin"
                      ref={this.passwordLoginRef}
                      onChange={this.updatePasswordValue}/>
                  </div>
                  <input type="submit" class="btn btn-primary btn-block" value="Login"/>
                  <div class="form-footer">
                    <a href="#"><small>Forgot your password?</small></a>
                  </div>
                  <div>
                    <small id="loginHelp" class="form-text text-danger">{this.state.loginError}</small>
                  </div>
                </form>
                </li>
              </ul>
            </li>
            <li class="nav-item">
              <a href="#" data-toggle="dropdown" class="btn btn-primary dropdown-toggle get-started-btn mt-1 mb-1">Sign up</a>
              <ul class="dropdown-menu form-wrapper">
                <li>
                  <form onSubmit={this.handleSignup}>
                    <p class="hint-text">Fill in this form to create your account!</p>
                    <div class="form-group">
                      <input
                        type="email"
                        class="form-control"
                        placeholder="Email"
                        required="required"
                        name="emailSignup"
                        onChange={this.handleSignupFieldChange}
                        ref={this.emailSignupRef}/>
                        <small id="emailHelp" class="form-text text-danger" style={{maxWidth:'250px',textAlign:'justify'}}>{this.state.emailError}</small>
                    </div>
                    <div class="form-group">
                      <input
                        type="text"
                        class="form-control"
                        placeholder="Username"
                        required="required"
                        name="usernameSignup"
                        ref={this.usernameSignupRef}/>
                    </div>
                    <div class="form-group">
                      <input
                        type="password"
                        class="form-control"
                        placeholder="Password"
                        required="required"
                        name="password1Signup"
                        onChange={this.handleSignupFieldChange}
                        ref={this.passwordSignupRef}/>
                        <small id="passwordHelp" class="form-text text-danger">{this.state.passwordError}</small>
                    </div>
                    <div class="form-group">
                      <input
                        type="password"
                        className="form-control"
                        placeholder="Confirm Password"
                        required="required"
                        name="password2Signup"
                        onChange={this.handleSignupFieldChange}
                        ref={this.passwordConfirmSignupRef}/>
                        <small id="passwordConfirmHelp" class="form-text text-danger">{this.state.passwordConfirmSignupError}</small>
                    </div>
                    <input
                      type="submit"
                      class="btn btn-primary btn-block"
                      value="Sign up"
                      onClick={this.validateEmailFormat}/>
                  </form>
                </li>
              </ul>
            </li>
          </ul>}</div>)
    }

}

Registration.propTypes = {
  authLogin: PropTypes.func.isRequired,
  authLogout: PropTypes.func.isRequired,
  user: PropTypes.array.isRequired
}

const mapStateToProps = state => ({
  user: state.user.user
});

export default connect(mapStateToProps,{authLogin, authLogout})(Registration);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...