我正在尝试围绕Redux重组我的React应用,并感到困惑。
one site说:
组件从其父级接收道具。这些道具不应该是 在组件内部进行了修改。
another说:
您过去可以使用setProps和replaceProps更改道具,但是 这些已被弃用。在组件的生命周期中道具 不应更改(考虑它们是不变的)。
因此,我应该考虑道具在组件的整个生命周期中都是不变的。好的,然后,替代方案是状态( React 状态,据我了解,它与 Redux 状态完全不同。好的...)
反应状态是组件的可变状态。它属于组件,与props不同,可以在组件的生命周期中进行更改。好。在Redux的上下文之外,我完全明白这一点。每个组件都拥有自己的可变状态,并将需要的东西作为道具传递给子对象,这对于子对象是不可变的。状态改变时,它将导致组件重新渲染其子代,并根据需要为其提供新的道具。
现在介绍Redux。 Redux存储为整个应用程序保持 single 状态。现在可以替换任何React状态吗? Redux状态的所有元素是否都作为道具传递给React组件? mapStateToProps
似乎暗示了这一点。那么我现在可以完全忘记React状态吗?
我认为,只要用户可以在屏幕上看到事物,组件的生命周期就可以持久。但是,如果props在组件的生命周期内不能更改,并且现在所有东西(从React的角度来看)都是props-难道意味着生命周期仅与Redux刷新其存储区一样长?
答案 0 :(得分:2)
露西·贝恩(Lucy Bain)的帖子很好地解释了一下,但请注意,setProps
的提及是React的非常的旧版本,而React 0.14的链接发行说明将这些描述为:仅对更新应用程序中的顶级组件有用。今天,如果您实际上需要更新根组件的道具,则可以第二次致电ReactDOM.render(<MyApp newProp={123} />)
。
有关一些经验法则的信息,请参见Redux FAQ entry on splitting state between Redux and React components,以帮助确定每个州应居住的位置。
我也鼓励您通读React-Redux documentation,以了解它们如何组合在一起。
答案 1 :(得分:2)
如丹·阿布拉莫夫(Dan Abramov)在其著名的You might not need redux文章中指出的那样, 仅当您需要整个应用程序中的 persistence 时才应使用Redux。当数据是临时性的(想想在简单视图中打开的选定手风琴)时,您可以安全地将状态存储在本地状态中。 所以:
立即(Redux)取代任何React状态吗? 如果愿意,可以。但这不是强制性的。
Redux状态的所有元素是否都作为道具传递给了React组件? 通常,是的。
那么我现在可以完全忘记React状态吗? 是的你可以。尽管不是强制性的,并且本地/ redux状态可以快乐地生活在一起。
[...]是否意味着生命周期只有Redux刷新其商店的时间长? 渲染发生在任何状态(本地或redux)更改时。
答案 2 :(得分:1)
我认为您首先需要了解的Redux周期如下:
Action Creator-> Action-> dispatch-> Reducers-> State
Action Creator是一个函数,它将返回一个纯JavaScript对象,称为Action。该操作将有一个type
和payload
,它们提供有关执行操作的上下文,例如,创建保险单,它看起来像这样:
// People dropping off a form (Action Creator)
const createPolicy = (name, amount) => {
return { // Action(a form in this analogy)
type: 'CREATE_POLICY',
payload: {
name: name,
amount: amount
}
};
};
所有动作创建者的外观都与您在此处看到的内容完全相同,只是略有不同。
但是我知道问题是要了解循环及其目的,而不是语法。
因此,您想了解动作创建者需要在React应用程序中扮演什么角色,然后再进入Reducers并弄清他们的角色,但是实际上在您找出了最困难的部分(动作创建者)之后,开始陷入困境。
dispatch
接收动作并将其分发给所有不同的Reducers
。 dispatch是Redux库本身的一部分,因此您无需将其写出来,而如果您使用mapStateToProps
帮助程序将它与高阶组件联系在一起,那么您对connect()
的疑问就可以解决了就像手动替代调度过程一样。
Reducers背后的想法是我们写出函数,每个函数都在应用程序内部建模不同的行为。每个reducer都会调用一个Action,reduce会检查该动作并确定是否基于该动作修改行为。
因此,诉讼创建者就像提起保险索赔的人,而诉讼就是实际的索赔。这些索赔归于称为减速器的这些不同部门。
因此,与保险索赔的类推类似,我可能要创建的减少器将是这样的索赔历史记录减少器:
// Reducers (Departments)
const claimsHistory = (oldListOfClaims, action) => {
};
因此,减速器的工作是确定它是否关心所接收的操作类型。如果它是type
createClaim
,那么我们要确保函数内的代码提取有效负载属性并将其添加到声明列表中,如果没有,则返回声明列表不变,这样看起来像这样:
// Reducers (Departments)
const claimsHistory = (oldListOfClaims, action) => {
if(action.type === 'CREATE_CLAIM') {
// we care about the action (claim)
return [...oldListOfClaims, action.payload]
}
// we dont care about the action (form)
};
所以我只是使用ES2015语法,该语法采用一个数组,获取所有记录,并将它们添加到全新的数组中,并添加新记录action.payload
。
这等效于先执行const numbers = [1,2,3]
然后执行[...numbers, 4]
,它输出:
(4) [1, 2, 3, 4]
或与oldListOfClaims.push(action.payload);
等效,但是此代码与我使用的代码有明显区别。
在我用于化简器的语法中,我正在创建一个全新的数组并向其中添加记录,而push()
方法正在修改现有数组。
我们始终希望避免在reducer内修改现有的数据结构。您将永远不会在减速器中看到push()
。
然后在某些情况下,我们并不关心该动作:
// Reducers (Departments)
const claimsHistory = (oldListOfClaims, action) => {
if(action.type === 'CREATE_CLAIM') {
// we care about the action (claim)
return [...oldListOfClaims, action.payload]
}
// we dont care about the action (form)
return oldListOfClaims;
};
接下来,您需要处理第一次调用reducer时将没有数据传递给它的情况。实际上,我们将收到undefined
的值。我们需要默认第一个参数的值。
// Reducers (Departments)
const claimsHistory = (oldListOfClaims = [], action) => {
if(action.type === 'CREATE_CLAIM') {
// we care about the action (claim)
return [...oldListOfClaims, action.payload]
}
// we dont care about the action (form)
return oldListOfClaims;
};
因此undefined
被一个空数组取代。
因此,在实现Redux以及随之而来的精细规则(例如确保您不修改传入的数组并确保始终返回一些值)时,弄清楚您的操作和简化器是困难的部分
reduce的总体目标是将传递给它的一些现有数据作为操作,并根据操作的内容查找并返回该现有数据。
因此,这是入门的所有基本内容。在所有这些中,您要返回Redux存储的新实例。
根据您的项目,您可能有1到3个或更多动作创建者和简化者。然后,将所有这些连接到一个称为存储的对象中,该对象只是不同动作和化简器的组合。
因此,您将在组件中的某处添加如下内容:const { createStore, combineReducer } = Redux;
您的combineReducers
如下所示:
const ourDepartments = combineReducers({
accounting: accounting,
claimsHistory: claimsHistory,
policies: policies
});
然后您就可以像这样完成商店的创建:
const store = createStore(ourDepartments);
store;
store
代表整个Redux应用程序。它包含对您所有不同减速器的引用,以及对这些减速器或数据产生的所有状态的引用。
store
对象具有一些有用的功能,例如dispatch
函数。为了调用dispatch()
,我们必须传递动作创建者创建的动作,然后将其传递给store.dispatch()
。
const store = createStore(ourDepartments);
const action = createPolicy('Alejandra', 35);
console.log(action);
store.dispatch();
以控制台方式退出action
时,您应该看到操作类型和有效负载:
{type: "CREATE_POLICY", payload: {…}}
然后您执行该操作并将其传递给store.dispatch()
,如下所示:
到目前为止,您可能已经看到一个错误,指出Action必须是一个普通的JavaScript对象,依此类推,那么一旦您通过以下操作,该错误就会消失:
store.dispatch(action);
您可以像这样检查以查看应用程序的状态:
console.log(store.getState());
{帐户:135,claimsHistory:Array(0),策略:Array(1)} 会计:135 ClaimsHistory:[] 政策:[“ Alejandra”] 原始:对象
现在您可以开始传递如下操作:
store.dispatch(createPolicy('Alejandra', 35));
store.dispatch(createPolicy('Ben', 20));
store.dispatch(createPolicy('Daniel', 78));
store.dispatch(createClaim('Alejandra', 120));
store.dispatch(createClaim('Ben', 50));
store.dispatch(deletePolicy('Daniel'));
console.log(store.getState());
控制台中的输出:
{帐户:63,claimsHistory:Array(2),策略:Array(2)} 会计:63 ClaimsHistory:Array(2)0:{name:“ Alejandra”, amountOfMoneyToCollect:120} 1:{名称:“ Ben”,valueOfMoneyToCollect: 50}的长度:2 原始:数组(0)策略:数组(2)0:“亚历杭德拉” 1:“本”长度:2 proto :数组(0) 原始:对象
这就是Redux库的工作方式
好的,所以我向您展示了它是如何独立工作的,但是Redux和React这两个库如何相互影响?
是的,通过Redux的实现,我们使用组件级别状态的频率要低得多。通常,我们通常将所有数据存储在Redux中。
在某些情况下,我们希望在Redux和React组件中都具有状态,但是通常,您现在在React应用程序中的所有状态现在都在Redux中,因此可以转换为一个更简单的React应用程序。 / p>
让我们说您有一些iTunes类型的应用程序,我们如何将它与Reactjs放在一起?
因此,这将是您的应用,而无需执行redux:
因此,这里的SongDetail
只需要知道您当前选择的歌曲是什么。因此,您的App
组件将它作为道具传递给SongDetail
,而SongDetail
会将其渲染到屏幕上。
仅使用React,这将是一个简单直接的应用程序。
那么您的应用程序如何通过Redux进行更改?
所以和以前一样,我们将有一个App
组件,一个SongList
和SongDetail
,但是App
组件的传递很少SongList
和SongDetail
下的信息。
相反,我们将抽象出这些想法,包括创建歌曲列表,选择歌曲以及将当前选择的歌曲放入redux应用程序中。
因此,您将拥有一个可产生歌曲列表的还原器和一个可记录当前所选歌曲内容的还原器。这就是应用程序中的两种状态。
最后,我们将确保我们有一个动作创建者以某种方式更改您的状态。这是在Redux应用程序中更改状态的唯一方法。
因此,您的动作创建者可能被称为“选择歌曲”,它将分派一个动作,并告诉所选歌曲还原器更新其数据并反映新选择的歌曲。
所以Redux就是您的应用。