我在项目中使用 react-boilerplate 结构。 我有一个容器LoginForm,该容器对用户进行身份验证并以状态存储来自API的响应。 我想从另一个容器NavHeader访问此响应,该容器是标题,它应该呈现这些信息。
NavHeader中的this.props
未定义。有人可以指出我在哪里出问题了。
更新:我试图将化极器和动作移至App(根)容器,仍然没有运气。显然,我没有做正确的事,也无法弄清楚。任何帮助将不胜感激!
export class UserLoginForm extends Component {
constructor(props) {
super(props);
}
onLoginSubmit(e) {
e.preventDefault();
const { email, password } = this.state;
this.props.dispatch(authorize(email, password));
}
render() {
return (
<HeroBannerStyle>
<form>
<InputField name="email" />
<InputField type="password" />
<Button onClick={this.onLoginSubmit}/>
</form>
</HeroBannerStyle>
);}}
const mapStateToProps = createStructuredSelector({
userLoginForm: makeSelectUserLoginForm(),
});
==> This essentially does this: state.get('userLoginForm', initialState);
const withConnect = connect(mapStateToProps);
const withReducer = injectReducer({ key: 'userLoginForm', reducer });
const withSaga = injectSaga({ key: 'userLoginForm', saga });
export default compose(withReducer,withSaga,withConnect(UserLoginForm);
export function authSuccess(payload) {
return {
type: AUTH_SUCCESS, payload: {user: payload},
};
}
export const authorize = (email, password) => ({
type: AUTH_REQUEST,
payload: {email, password},
});
export const initialState = fromJS({
user: null
});
const userLoginFormReducer = (state = initialState, { type, payload }) => {
switch (type) {
case AUTH_SUCCESS: {
return {
...state,
user: payload.data,
};
}
default:
return state;
}
};
export default userLoginFormReducer;
function* fetchUserReqSaga({ payload: { email, password } }) {
try {
const response = yield call(fetchUser, { email, password });
yield put({ type: AUTH_SUCCESS, payload: response });
} catch (error) {}
}
function fetchUser({ email, password }) {
return usersAPI.post('/login', {email, password,});
}
class NavHeader extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log('this.props..', this.props);//THIS IS UNDEFINED
}
render() {
return (
<div>{this.props.user}</div> // THIS IS EMPTY
);
}}
const mapStateToProps = state => {
console.log(state); //THIS DOES NOT HAVE 'userLoginForm'
return {
user: state.get('userLoginForm')
}
};
const withConnect = connect(mapStateToProps);
const withReducer = injectReducer({ key: 'navHeader', reducer });
const withSaga = injectSaga({ key: 'navHeader', saga });
export default compose(withReducer,withSaga,withConnect)(NavHeader);
答案 0 :(得分:0)
组件和(rootReducer,rootSaga)连接到商店,所有动作和道具都通过商店来进行。您可以在浏览器中进行调试,以查看数据的运行情况。
这是官方文档。这很容易而且很短,但是了解所有这些reduce,action,props东西至关重要。还有一个小的codepen片段,以了解其工作原理。
这也是类似项目的摘录。从断点处添加了更多行。
UPD:
它从连接到存储的rootReducer中获取数据。初始化存储并连接所有减速器时,将调用CombineReducers:
export interface RootState {
books: BooksState;
authors: AuthorsState;
}
const rootReducer: Reducer<RootState> = combineReducers<RootState>({
authors: authorReducer,
books: booksReducer
})
export default rootReducer;
减速器返回有效载荷(数据)后,由选择器获取:
export const getData = (state: RootState) => state.books.data
它用于mapStateToProps中,我使用 connect 调用高阶函数,并从存储中收集数据并创建容器。 Compose不是Redux,如果使用它,则必须检查中间件的顺序(compose(m1,m2,m3 ...)从右到左):
const mapStateToProps = (state: RootState) => ({
books: getData(state),
pagination: getPagination(state),
loading: getLoadingStatus(state),
error: getError(state),
})
export default connect(mapStateToProps, { loadBooks, searchBook, removeBook, addBook, editBook, sortBook,
})(FilterableBooksTable);
然后涉及“聪明”组件:
export interface BooksTableProps {
readonly books: Array<Book>;
loadBooks: (pagination: Pagination) => object;
}
export interface BookFormProps {
addBook: (name: string, author: string, cost: number, genre: string) => object;
}
type FilterableBooksTableProps = BooksTableProps & BookFormProps;
export default class FilterableBooksTable extends React.Component<FilterableBooksTableProps> {
render() {
const {
books,
loadBooks,
addBook,
} = this.props
return (
<div>
<Row>
<Col span={4}>
<BookForm
addBook={addBook}
>
</Col>
<Col span={16}>
<BookTable
books={books}
loadBooks={loadBooks}
/>
</Col>
</Row>
</div>
)
}
最后数据到达“愚蠢”组件:
render() {
const {
books
} = this.props;
return (
<div>
<Table
dataSource={books}
...
/>
</div>
)
}
我不知道您的商店配置是什么,所以我将添加与所有sagas,reducer相连的我的矿井:
import { Store, createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import { composeWithDevTools } from 'redux-devtools-extension';
import rootReducer, { RootState } from '@redux/rootReducer';
import rootSaga from '@redux/sagas';
export default function configureStore(initialState?: RootState): Store<RootState> {
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
const composeEnhancers = composeWithDevTools({});
const enhancer = composeEnhancers(
applyMiddleware(...middlewares)
);
const store = createStore(
rootReducer,
initialState!,
enhancer,
);
if (module.hot) {
module.hot.accept('@redux/rootReducer', () => {
const nextRootReducer = require('@redux/rootReducer').default;
store.replaceReducer(nextRootReducer);
});
}
sagaMiddleware.run(rootSaga);
return store;
}