我在下面发布了一个答案,但是如果有人可以解释为什么这样做是必要的,那么您会得到赏金,我经历了一个redux教程,觉得我没有了解mapDispatchToProps
,只有{{1 }}。如果您可以更深入地解释mapStateToProps
和mapStateToProps
到底在做什么以及它们之间有何不同,我将为您提供赏金。
我有一个看起来像mapmapToProps函数
mapDispatchToProps
在我的const mapStateToProps = state => {
return {
firstName: state.firstName,
middleName: state.middleName,
lastName: state.lastName,
}
}
const ReduxTabForm = connect(mapStateToProps)(MyTab)
组件中,如果没有在这2个字段中输入任何内容,则该按钮应该处于不活动状态,但是该按钮是否被禁用的状态不会改变
MyTab
该警报语句在页面刷新时执行,但是在我输入数据之后不执行任何时间。我已经检查了redux状态是否正在更新。但是该按钮不会更新,并且isDisabled函数不会运行多次
答案 0 :(得分:1)
我不知道这是否可以解决您的问题,但是也许这是下面的事情之一: 1-正如穆罕默德·费萨尔(Mohammad Faisal)所说,正确的呼叫道具形式应该是
const { firstName, lastName } = props;
2-代替reduxTabForm,也许您可以使用它:
export default connect(mapStateToProps)(MyTab);
3-最后,也许是“ isDisabled”中的错误:
for(let i = 0; i < requiredFields.length; i=i+1){
if (!requiredFields){
return false
}
}
如果仔细看,您会发现您没有检查requeiredFields内部是否有错误,您正在寻找是否不存在错误if (!requiredFields)
,可能将条件更改为if(!requiredFields[i])
以便进行检查每个变量,如果数组不存在则不是。
编辑:返回条件是否正确?不存在时返回False?
答案 1 :(得分:1)
const ReduxTabForm = connect(mapStateToProps,null)(MyTab)
请尝试使用此代码段,因为您没有将调度功能传递给组件。最好传递空值。 mapdispatchtoProps与mapStateToProps的基本原理相同。您将函数存储在商店中(通常存储在动作中),并且在渲染组件时将这些函数附加到道具中。渲染组件后,您将能够运行商店中的功能。
答案 2 :(得分:1)
我查看了您的reducer代码,它看起来像这样:
...
const reducer = combineReducers({
formData: formReducer,
})
export default reducer
这意味着您的redux状态结构如下:
state = {
formData: {
firstName: <value>,
middleName: <value>,
lastName: <value>,
}
}
因此,要使组件在redux状态更改时重新呈现,需要在mapStateToProps函数中预订正确的状态变量。将其更改为此,将起作用:
const mapStateToProps = state => {
return {
firstName: state.formData.firstName, // Note that I added "formData"
middleName: state.formData.middleName,
lastName: state.formData.lastName
}
}
旁注:
console.log
优于alert
。 React DevTools和Redux DevTools甚至更好。答案 3 :(得分:0)
您的状态值将作为prop传递到您的组件。因此,如果要在组件内部访问它们,则应执行以下操作
const {firstName, lastName} = props
答案 4 :(得分:0)
您可以做的事情是这样的:
{{1}}
这样,如果您的任何字段都是虚假的,则按钮将被禁用。 (空字符串是虚假的)
答案 5 :(得分:0)
React redux documentation also explains mapDispatchToProps
如果您从setFormData
调用名为redux/actions.js
的动作,则将把动作setFormData
(是一个对象)映射到组件。操作setFromData
代替mapDispatchToProps
。这就是文档中关于将操作映射到您的组件的内容
如果该对象充满了动作创建者,则每个动作创建者将 变成了一个prop函数,该函数自动调度其动作
要解决您的问题,请将您的connect函数调用更改为此。
const ReduxApp = connect(
setFormData,
mapStateToProps
)(App)
答案 6 :(得分:0)
您的问题出在此代码isDisabled()
const isDisabled = () => {
const {firstName, lastName} = store.getState().form
const requiredFields = [firstName, lastName]
alert(requiredFields)
for(let i = 0; i < requiredFields.length; i=i+1){
if (!requiredFields){
return false
}
}
return true
}
您正在尝试在循环!requiredFields
中进行测试,该循环是您创建的一个数组,即使该数组没有值,该数组也始终返回false。这是参考类型。您现在可以做的是
const isDisabled = () => {
const {firstName, lastName} = store.getState().form
const requiredFields = [firstName, lastName]
alert(requiredFields)
for(let i = 0; i < requiredFields.length; i=i+1){
if (!requiredFields[i]){
return false
}
}
return true
}
您的循环将检查firstNAme
和LastName
的值是否未定义,测试应对此进行响应。
答案 7 :(得分:0)
问题出在mapStateToProps
方法中。您的代码将firstNAme
和lastName
设置为formData
在状态对象之内,并且您正尝试直接从状态对象访问它。
此codesandbox的项目已使用修复程序进行了更新。我没有在您的代码中修复任何其他问题,因此一切都只有mapStateToProps
应该是:
const mapStateToProps = (state) => {
return {
firstName: state.formData["firstName"],
middleName: state.middleName,
lastName: state.formData["lastName"]
};
};
,这将解决您的问题。 mapStateToProps
是一种投影功能,可以将整个商店投影到某个对象,该对象仅具有基于组件要求的属性。
答案 8 :(得分:0)
Redux仅在存储状态更改时才重新渲染组件。检查您是否仅更新商店的state属性而不是整个状态,因为redux通过比较它们的引用来比较状态,因此,如果您在reducer内执行以下操作:
State.firstName = action.payload.firstName;
return State;
然后将其更改为:
return {...State, firstName: action.payload.firstName}
注意:如果您无法理解这些信息,请也提供您的减速器代码,以便我了解您如何更新商店状态。
答案 9 :(得分:0)
在GitHub上查看MRE,我想说的第一件事是您的按钮将只有一个状态,并且是刷新页面时isDisabled()
方法返回的状态。这是因为每次您在输入字段上书写时,App组件都不会刷新,因此您将永远无法对其进行更改。
您需要在mapStateToProps函数中订阅正确的状态变量。像这样:
const mapStateToProps = state => {
return {
firstName: state.formData.firstName,
middleName: state.formData.middleName,
lastName: state.formData.lastName
}
}
function App({firstName, lastName}) { ...
reducer.js
中创建化简器时,您没有初始化状态。如果不这样做,则firstName
和lastName
的初始状态将为空。这是您应该如何做:import {combineReducers} from 'redux'
import {SET_FORM_DATA} from './actions'
const initialState = {
firstName: "",
lastName: ""
}
const formReducer = (state = initialState, action) => {
if (action.type === SET_FORM_DATA){
return {
...state,
...action.payload
}
}else{
return state
}
}
const reducer = combineReducers({
formData: formReducer,
})
export default reducer
function App({firstName, lastName}) {
return (
<div className="App">
<div className='bg-light rounded'>
<div className='px-sm-5 pt-0 px-4 flexCenterCol mx-5'>
<div>
<input
type='text'
className="form-control"
placeholder="First Name"
onChange={(e) => {
store.dispatch(setFormData({'firstName': e.target.value}));
console.log(firstName === "h");
}}
></input>
<input
type='text'
className="form-control"
placeholder="Last Name"
onChange={(e) => {
store.dispatch(setFormData({'lastName': e.target.value}))
alert(JSON.stringify(store.getState()))
}}
></input>
</div>
<button
type="submit"
disabled={firstName == "" || lastName == ""}
>
Button
</button>
</div>
</div>
</div>
);
}
因此,通过这种方式,您将能够更新商店中的状态,并具有您正在寻找的动态行为。
答案 10 :(得分:0)
在isDisabled
函数中,您从form
读取数据:const {firstName, lastName} = store.getState().form
,但我想它们已保存到formData
。
将const {firstName, lastName} = store.getState().form
更改为const {firstName, lastName} = store.getState().formData
应该会有所帮助。