Привет! Я работаю над 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);