Javascript 使用连接的React路由器在React+Redux中推送状态
我想在子Reddit发生更改时将状态推送到浏览器并附加到路径名 在下面的示例中,用户从['reactjs','frontend']中选择一个选项。因此,当用户选择reactjs时,我想根据选择将浏览器url更改为:/reddit/reactjs或/reddit/frontend 因此,当用户来回移动时,我希望显示已经获取的数据 对于下面的示例,如何使其与react redux一起工作?正常情况下,我用的是历史 注意:我正在使用连接的路由器 index.js: reducers.js: configureStore.js Root.jsJavascript 使用连接的React路由器在React+Redux中推送状态,javascript,reactjs,redux,react-redux,Javascript,Reactjs,Redux,React Redux,我想在子Reddit发生更改时将状态推送到浏览器并附加到路径名 在下面的示例中,用户从['reactjs','frontend']中选择一个选项。因此,当用户选择reactjs时,我想根据选择将浏览器url更改为:/reddit/reactjs或/reddit/frontend 因此,当用户来回移动时,我希望显示已经获取的数据 对于下面的示例,如何使其与react redux一起工作?正常情况下,我用的是历史 注意:我正在使用连接的路由器 index.js: reducers.js: confi
我把它放在handleChange方法中。当选择器更改时,我将状态推送到浏览器。但是,当我来回移动时,数据不会根据此url进行更改。我在每个州都看到相同的数据。我们可以使用history属性处理这个场景。我们使用历史侦听器实现,并使用location属性,后者反过来提供路径名。它将在componentDidUpdate中实现。每次单击浏览器的“后退”和“前进”按钮时,都会调用侦听器,并相应地更改服务调用和状态 AsyncApp.js
我设法将当前路径推送到浏览器,但它无法处理获取的数据。当我返回或前进时,我看到相同的数据。您能否详细说明一下如何使用上面的示例填充以前的状态数据?我已经更新了这个问题。我在网上看到过一些示例,但在上面的例子中,没有一个是使用Redux的。所以,当你使用back和forward时,数据应该通过服务调用进行相应的更改,状态也应该并行地更改。这就是我所要求的。我没能成功。对以上样品有什么帮助吗?如果你举例说明一个工作示例,我会更好地理解并发现我的错误。理解。。如果你提供你的代码的演示,我对它进行修改,如果你仍然感兴趣的话,我将与你分享uIf,那就太好了。我做了一个工作样本。演示:其可编辑代码如下:只需在选项之间切换,然后返回/前进。您将看到url更改,但内容相同。您应该将您的帖子组件连接到redux,并根据您选择的选项获取帖子。对此我有点不知所措,我想我也尝试过,但没有成功。可能我测试错误。你能给我看一段代码吗?我将非常感谢:/n另外,AsnycApp.js已连接。你不是这么说的吗?例如,若我手动单击一个链接,我可以在浏览器中导航,但获取的ajax数据不会得到反映。
import 'babel-polyfill'
import React from 'react'
import { render } from 'react-dom'
import Root from './containers/Root'
render(<Root />, document.getElementById('root'))
import fetch from 'cross-fetch'
export const REQUEST_POSTS = 'REQUEST_POSTS'
export const RECEIVE_POSTS = 'RECEIVE_POSTS'
export const SELECT_SUBREDDIT = 'SELECT_SUBREDDIT'
export const INVALIDATE_SUBREDDIT = 'INVALIDATE_SUBREDDIT'
export function selectSubreddit(subreddit) {
return {
type: SELECT_SUBREDDIT,
subreddit
}
}
export function invalidateSubreddit(subreddit) {
return {
type: INVALIDATE_SUBREDDIT,
subreddit
}
}
function requestPosts(subreddit) {
return {
type: REQUEST_POSTS,
subreddit
}
}
function receivePosts(subreddit, json) {
return {
type: RECEIVE_POSTS,
subreddit,
posts: json.data.children.map(child => child.data),
receivedAt: Date.now()
}
}
function fetchPosts(subreddit) {
return dispatch => {
dispatch(requestPosts(subreddit))
return fetch(`https://www.reddit.com/r/${subreddit}.json`)
.then(response => response.json())
.then(json => dispatch(receivePosts(subreddit, json)))
}
}
function shouldFetchPosts(state, subreddit) {
const posts = state.postsBySubreddit[subreddit]
if (!posts) {
return true
} else if (posts.isFetching) {
return false
} else {
return posts.didInvalidate
}
}
export function fetchPostsIfNeeded(subreddit) {
return (dispatch, getState) => {
if (shouldFetchPosts(getState(), subreddit)) {
return dispatch(fetchPosts(subreddit))
}
}
}
import { combineReducers } from 'redux'
import {
SELECT_SUBREDDIT,
INVALIDATE_SUBREDDIT,
REQUEST_POSTS,
RECEIVE_POSTS
} from './actions'
function selectedSubreddit(state = 'reactjs', action) {
switch (action.type) {
case SELECT_SUBREDDIT:
return action.subreddit
default:
return state
}
}
function posts(
state = {
isFetching: false,
didInvalidate: false,
items: []
},
action
) {
switch (action.type) {
case INVALIDATE_SUBREDDIT:
return Object.assign({}, state, {
didInvalidate: true
})
case REQUEST_POSTS:
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false
})
case RECEIVE_POSTS:
return Object.assign({}, state, {
isFetching: false,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
})
default:
return state
}
}
function postsBySubreddit(state = {}, action) {
switch (action.type) {
case INVALIDATE_SUBREDDIT:
case RECEIVE_POSTS:
case REQUEST_POSTS:
return Object.assign({}, state, {
[action.subreddit]: posts(state[action.subreddit], action)
})
default:
return state
}
}
const rootReducer = combineReducers({
postsBySubreddit,
selectedSubreddit
})
export default rootReducer
import { createStore, compose, applyMiddleware } from 'redux'
import { createBrowserHistory } from 'history'
import { routerMiddleware } from 'connected-react-router'
import thunkMiddleware from 'redux-thunk'
import logger from 'redux-logger'
import rootReducer from '../reducers'
// const loggerMiddleware = createLogger()
export const history = createBrowserHistory()
export default function configureStore(preloadedState?: any) {
const store = createStore(
rootReducer(history), // root reducer with router state
preloadedState,
compose(
applyMiddleware(
thunkMiddleware,
logger,
routerMiddleware(history), // for dispatching history actions
// ... other middlewares ...
),
),
)
return store
}
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import configureStore from '../configureStore'
import AsyncApp from './AsyncApp'
const store = configureStore()
export default class Root extends Component {
render() {
return (
<Provider store={store}>
<AsyncApp />
</Provider>
)
}
}
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import {
selectSubreddit,
fetchPostsIfNeeded,
invalidateSubreddit
} from '../actions'
import Picker from '../components/Picker'
import Posts from '../components/Posts'
class AsyncApp extends Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.handleRefreshClick = this.handleRefreshClick.bind(this)
}
componentDidMount() {
const { dispatch, selectedSubreddit } = this.props
dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
componentDidUpdate(prevProps) {
if (this.props.selectedSubreddit !== prevProps.selectedSubreddit) {
const { dispatch, selectedSubreddit } = this.props
dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
}
handleChange(nextSubreddit) {
this.props.dispatch(selectSubreddit(nextSubreddit))
this.props.dispatch(fetchPostsIfNeeded(nextSubreddit))
}
handleRefreshClick(e) {
e.preventDefault()
const { dispatch, selectedSubreddit } = this.props
dispatch(invalidateSubreddit(selectedSubreddit))
dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
render() {
const { selectedSubreddit, posts, isFetching, lastUpdated } = this.props
return (
<div>
<Picker
value={selectedSubreddit}
onChange={this.handleChange}
options={['reactjs', 'frontend']}
/>
<p>
{lastUpdated && (
<span>
Last updated at {new Date(lastUpdated).toLocaleTimeString()}.{' '}
</span>
)}
{!isFetching && (
<button onClick={this.handleRefreshClick}>Refresh</button>
)}
</p>
{isFetching && posts.length === 0 && <h2>Loading...</h2>}
{!isFetching && posts.length === 0 && <h2>Empty.</h2>}
{posts.length > 0 && (
<div style={{ opacity: isFetching ? 0.5 : 1 }}>
<Posts posts={posts} />
</div>
)}
</div>
)
}
}
AsyncApp.propTypes = {
selectedSubreddit: PropTypes.string.isRequired,
posts: PropTypes.array.isRequired,
isFetching: PropTypes.bool.isRequired,
lastUpdated: PropTypes.number,
dispatch: PropTypes.func.isRequired
}
function mapStateToProps(state) {
const { selectedSubreddit, postsBySubreddit } = state
const { isFetching, lastUpdated, items: posts } = postsBySubreddit[
selectedSubreddit
] || {
isFetching: true,
items: []
}
return {
selectedSubreddit,
posts,
isFetching,
lastUpdated
}
}
export default connect(mapStateToProps)(AsyncApp)
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class Picker extends Component {
render() {
const { value, onChange, options } = this.props
return (
<span>
<h1>{value}</h1>
<select onChange={e => onChange(e.target.value)} value={value}>
{options.map(option => (
<option value={option} key={option}>
{option}
</option>
))}
</select>
</span>
)
}
}
Picker.propTypes = {
options: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired
}
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class Posts extends Component {
render() {
return (
<ul>
{this.props.posts.map((post, i) => (
<li key={i}>{post.title}</li>
))}
</ul>
)
}
}
Posts.propTypes = {
posts: PropTypes.array.isRequired
}
import { push } from 'connected-react-router';
...
handleChange(nextSubreddit) {
this.props.dispatch(push('/reddit/' + nextSubreddit))
}
// code here
import { history } from '../configureStore'
// code here
componentDidUpdate(prevProps) {
if (this.props.selectedSubreddit !== prevProps.selectedSubreddit) {
const backBrowser = history.listen(location => {
console.log(location.pathname)
// code here
}
// code here
}
}