我正在使用ReactJS,Redux(带有服务器端呈现)和react-router-redux as set up here,并且由于路由如何与其余的redux状态和操作一起工作而受到一点抛出。
例如,我有一个包含路径/members
的成员组件:
class Members extends Component {
static need = [
fetchMembers
]
render() {
...
static need
数组指定一个操作,该操作在状态上填充数组,然后映射到组件props。那很有用。
但后来我有了一个路由为members/:memberId
的个人成员组件。如何以客户端和服务器端的方式加载该单个成员。
我现在所做的是相同的:
class Member extends Component {
static need = [
fetchMembers
]
render() {
...
然后只映射单个成员
function mapStateToProps(state, ownProps) {
return {
member: state.member.members.find(member => member.id == ownProps.params.memberId),
};
}
这有效,但显然是错误的。所以问题是双重的:
当用户单击具有查询参数(:memberId)的路由器Link
时,如何使用该路由器参数查询特定文档(假设为mongo数据库)。我是否会以某种方式触发一个单独的操作来填充redux状态的active
成员字段?在路径组件componentDidMount
中,这发生了什么?
这如何与服务器端呈现一起使用?
答案 0 :(得分:1)
我有同样的问题,似乎找到了一种适合我的设置的方法。我使用Node,Express,React,React Router,Redux和Redux Thunk。
1)这实际上取决于数据的位置。如果/member/:memberId
所需的数据已处于状态(例如,来自之前的调用),理论上您可以过滤componentDidMount
被触发时已有的数据。
然而,我宁愿将事情分开,以避免头痛。在整个应用程序中开始使用一个数据源用于多个目的地/目的可能会让您在漫长的一天(例如,组件A 需要更多/更少的关于该成员的属性而不是组件B 或组件A 需要的格式与组件B 等格式不同。)
这个决定当然应该基于你的用例,但由于现在API调用的成本,当有人导航到/member/:memberId
时,我不会害怕(根本)做出一个。
2)我将使用我的典型设置的简化版本来回答:
每当请求通过时,我都会让这个家伙处理它。
// Imports and other jazz up here
app.use((req, res) => {
const store = configureStore({});
const routes = createRoutes(store);
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
if (error) {
res.status(500).send(error.message);
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
const fetchedData = renderProps.components
.filter(component => component.fetchData)
.map(component => component.fetchData(store, renderProps.params));
Promise.all(fetchedData).then(() => {
const body = renderToString(
<Provider store={store}>
<RouterContext {...renderProps} />
</Provider>
);
res.status(200).send(`<!doctype html>${renderToStaticMarkup(
<Html
body={body}
state={store.getState()}
/>)
}`);
});
} else {
res.status(404).send('Not found');
}
});
});
它将在即将呈现的组件上查找fetchData
,并确保在向客户端发送任何内容之前获取数据。
在每条路线上,我都有Container
。 Container
的唯一目的是收集该路线所需的数据。正如您所提到的,这可能发生在服务器端(在我的情况下为fetchData
)或客户端(在我的情况下为componentDidMount
)。我的典型Container
看起来像这样:
// Imports up here
class Container extends Component {
static fetchData(store, params) {
const categories = store.dispatch(getCategories());
return Promise.all([categories]);
}
componentDidMount() {
this.props.dispatch(getCategoriesIfNeeded());
}
render() {
return this.props.categories.length ? (
// Render categories
) : null;
}
}
Container.propTypes = {
categories: PropTypes.array.isRequired,
dispatch: PropTypes.func.isRequired,
params: PropTypes.object.isRequired,
};
function mapStateToProps(state) {
return {
categories: state.categories,
};
}
export default connect(mapStateToProps)(Container);
在上面的Container
我使用getCategories
和getCategoriesIfNeeded
来确保我拥有该路线所需的数据。 getCategories
仅称为服务器端,getCategoriesIfNeeded
仅称为客户端。
请注意,我params
和fetchData
(从componentDidMount
传递)connect()
可用,我可能会用它来提取:memberId
之类的内容
以下列出了用于获取上述数据的两个函数:
// Using this for structure of reducers etc.:
// https://github.com/erikras/ducks-modular-redux
//
// actionTypes object and reducer up here
export function getCategories() {
return (dispatch, getState) => {
dispatch({
type: actionTypes.GET_REQUEST,
});
return fetch('/api/categories').then(res => {
return !res.error ? dispatch({
error: null,
payload: res.body,
type: actionTypes.GET_COMPLETE,
}) : dispatch({
error: res.error,
payload: null,
type: actionTypes.GET_ERROR,
});
});
};
}
export function getCategoriesIfNeeded() {
return (dispatch, getState) => {
return getState().categories.length ? dispatch(getCategories()) : Promise.resolve();
};
}
如上所示,我dispatch
和getState
都可以使用Redux Thunk - 这也可以处理我的承诺 - 这让我可以自由使用我已有的数据,请求新数据和做我的减速机的多次更新。
我希望这足以让你感动。如果不是,请不要犹豫,要求进一步解释:)
答案 1 :(得分:0)
事实证明,答案非常简单。从Isomorphic Redux App实现的实现通过将路由查询参数传递给动作创建者,将组件上的need
静态属性绑定回路由器。
所以对于路线:
items/:id
您使用
之类的组件class Item extends Component {
static need = [
fetchItem
]
render() {
指定它需要fetchItem
操作。该操作通过了路径的查询参数,您可以像
export function fetchItem({id}) {
let req = ...
return {
type: types.GET_ITEM,
promise: req
};
}
有关此工作原因的更详细解释,请阅读marcfalk的答案,其中描述了一种非常类似的方法。