我有一个显示所选帖子的组件PostsShow
:
@connect((state) => ({post: state.posts.post}), {fetchPost})
class PostsShow extends Component {
componentDidMount() {
this.props.fetchPost(this.props.match.params.id);
}
render() {
const { post } = this.props;
if (!post) {
return <div></div>;
}
return (
<div>
<Link to='/'>Back</Link>
<h3>{post.title}</h3>
<h6>Categories: {post.categories}</h6>
<p>{post.content}</p>
</div>
);
}
}
问题是,当用户第一次访问该页面时,fetchPost
函数使用与所选帖子相关联的一些数据填充州部分(posts.post
),当用户选择其他帖子时,他可以查看旧数据1-2秒(在新请求完成之前)。
动作图:
Back
按钮我是整个redux概念的新手,所以我很好奇你是如何避免这种行为的呢?
我的解决方案:
我假设您可以创建一个switch分支,它将使用posts.post
值修改状态(null
part)并在componentWillUnmount
方法上触发此行为。所以:
动作:
export function clearPost() {
return {
type: CLEAR_POST,
payload: null
}
}
减速机:
const INITIAL_STATE = { all: [], post: null };
export default function (state = INITIAL_STATE, action) {
switch (action.type) {
// ... Other cases
case CLEAR_POST:
return { ...state, post: null }
default:
return state;
}
}
组件:
class PostsShow extends Component {
componentDidMount() {
this.props.fetchPost(this.props.match.params.id);
}
componentWillUnmount() {
this.props.clearPost();
}
render() {
// Old render
}
}
这是与redux反应的好方法吗?
答案 0 :(得分:1)
你的州结构并不理想。试着像这样保留你的帖子:
posts: {
byID: {
1: {/*the post* no 1/},
2: {/*the post* no 2/},
// ...
}
allIDs: [2, 1 /*, ...*/],
}
通过这种方式,您可以为列表视图提供帖子ID的有序列表,并通过以下状态显示单个帖子:this.posts.byID['thePostID']
。
另请参阅how to normalize state上的redux文档。
这也可以解决您的问题,因为当您从商店获取具有尚不存在的ID的帖子时,它将undefined
呈现为空div
。加载指示器将是最好的展示。
答案 1 :(得分:0)
这是因为您正在进行异步调用,这需要时间来获取新日期,这就是为什么当获取帖子获取新数据时,您的组件将更新并呈现它。 您可以有两种方法: 1)您可以创建一个异步函数,该函数将调度一个动作,该动作将使用有效负载更新减速器
status :LOADING,
一旦fetch函数返回新数据,再次调度一个动作 将更新reducer更新为
status:SUCCESS
并在您的组件中检查商店中道具收到的状态是否为“加载”#39;或者&#39; SUCCESS&#39;如果SUCCESS然后显示新状态,如果LOADING继续显示某些文本,如组件正在加载:
这是示例
SomeComponent.js
export default class SomeComponent extends Component {
constructor (props) {
super(props)
this.model = {}
}
render () {
const {
status
} = this.props
const loading = status === 'LOADING'
return (
<Layout>
<Row>
<FormField
label='First Name'
id='first-name'
value={details.firstName}
onChange={this.update('firstName’)}
disabled={loading}
mandatory
/>
</Row>
</Layout>
)
}
}
actions.js
const fetchDetails = (someId) => {
return (dispatch) => {
dispatch(dataFetching())
return http.get(dispatch, `https//someCall`, null, {
'Accept': SOME_HEADER
}).then((data) => {
dispatch(dataFetched(data))
}).catch((error) => {
dispatch(someError(`Unable to retrieve the details ${error}`))
})
}
}
const dataFetching = () => ({
type: constants.DATA_FETCHING
})
const dataFetched = (data) => ({
type: constants.DATA_FETCHED,
data
})
Reducer.js
export default function reducer (state = {}, action = {}) {
switch (action.type) {
case constants.DATA_FETCHING:
return Object.assign({}, state, {
data: {},
status: 'LOADING'
})
case constants.DATA_FETCHED:
return Object.assign({}, state, {
data: action.data,
status: 'SUCCESS'
})
default:
return state
}
}
第二种方法 你做过的那个,但为了清楚起见,试着使用第一个。