I was doing a bit of refactoring and tried connecting a higher level component to redux using connect() but the component I'm connecting keeps giving me empty props.
I've included the relevant code, I've structured my redux reducers into a ducks format, so the actions/creators and reducers are in one module file.
The files are containers/login.js, presentation/login.js, presentation/logins.js, app.js and the root index.js.
When I decided to rename some actions, files and reducers, moved the connect to a higher component, the connection stopped working and now I have empty props.
Help much appreciated.
// containers/login.js
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom'
import { fetchPage } from '../redux/modules/Login';
import Login from '../presentation/Login';
const mapStateToProps = (state) => {
return {
page: state.page,
forms: state.forms
}
}
const mapDispatchToProps = (dispatch) => {
return {
fetchPage: () => dispatch(fetchPage())
} // here we're mapping actions to props
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Login);
// redux/modules/login.js
import fetch from 'cross-fetch';
const RECIEVE_FORM = 'RECIEVE_FORM';
export const receiveForm = (response) => ({
type: RECIEVE_FORM,
forms: response.forms
})
const initialState = {
page: "",
forms: []
}
// MIDDLEWARE NETWORK REQUEST DISPATCHER
export const fetchPage = () => {
return dispatch => {
return fetch('http://localhost:3001/login')
.then(
response => response.json(),
)
.then(
response => dispatch(receiveForm(response))
)
}
}
// REDUCER COMPOSITION CALL EXISTING REDUCERS
// REDUCER COMPOSITION PATTERN
// ACCUMULATIVE ACTION REDUCER
export default function Login(state = initialState, action){
switch (action.type){
case RECIEVE_FORM:
return {
...state,
forms: action.forms
}
default:
return state;
}
}
// presentation/login.js
import React, { Component } from 'react';
import styled from 'styled-components';
import Wrapper from '../components/Wrapper';
import Card from '../components/Card';
import Text from '../components/Text';
import Logo from '../components/Logo';
import FormGroup from '../components/FormGroup';
const WrapperLogin = styled(Wrapper)`
.login__card{
padding: 4.5rem 2.5rem 2rem 2.5rem;
}
`;
const BoxLogo = styled.div`
.login__logo{
display: block;
margin: 0 auto;
}
`;
export default class Login extends Component{
componentDidMount() {
console.log(this.props)
//this.props.fetchPage();
}
render(){
return(
<main>
<WrapperLogin className="login">
<Card className="login__card">
<BoxLogo>
<Logo className="login__logo" width={187.36} height={76.77} />
</BoxLogo>
<FormGroup name="login" className="login_formGroup" />
</Card>
<Text primitive="p" margin='4px 0 0 0' size="0.8rem" textAlign="center" display='block'>Brought to you by WORLDCHEFS</Text>
</WrapperLogin>
</main>
)
}
}
// app.js
// manage routes here
//import _ from 'lodash';
import React, { Component } from 'react'
import { BrowserRouter as Router, Route, Redirect } from 'react-router-dom';
import { ThemeProvider } from 'styled-components';
import Login from './presentation/Login';
type Props = {
}
type State = {
mode: string
};
export default class App extends Component <Props, State> {
constructor(){
super();
this.state = {
...this.state,
mode: 'mobile'
}
}
render(){
return(
<ThemeProvider theme={{ mode: this.state.mode }}>
<Login />
</ThemeProvider>
)
}
}
// root
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import configureStore from './redux/configureStore';
import registerServiceWorker from './registerServiceWorker';
import App from './App';
import { injectGlobal } from 'styled-components';
import styles from './assets/styles';
const store = configureStore();
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root')
);
The reason your props in Login component are empty is because you are not actually using the connected Login container, As you have mentions its in containers/login
So in your App.js change the import of login from ./presentation/login to
import Login from '/path/to/containers/Login';
You have imported presentation component in your app.js rather than container component. Please import your container component like below
import Login from './containers/login.js';
This will solve the problem as per my understanding from your code
Related
For some reason my "secondComponent" isn't actually having mapStateToProps invoked at all, even with a console.log() in it and I am actually quite confused as to why this might be. Redux is working perfectly with my "App" component, but without mapStateToProps being called in the child component it's left in the dark.
Hoping someone could help me here!
1: Index.tsx, where I do make sure to have a Provider for the store.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux';
import configureStore from './store';
const store = configureStore;
ReactDOM.render(
<Provider store={store()}>
<App />
</Provider>,
document.getElementById('root')
);
2: My "App" component that works with the redux store without issue.
import * as React from 'react';
import { connect } from 'react-redux';
import { addItemToCart, removeItemFromCart } from './store/cart/actions';
import { Button, Grid } from 'semantic-ui-react';
import { RootState } from './store';
import { SecondComponent } from './components/SecondComponent';
export interface IAppProps {
addItemToCart: typeof addItemToCart,
removeItemFromCart: typeof removeItemFromCart,
userId: number,
selectedItems: number[]
}
export class App extends React.Component<IAppProps> {
onClickAdd() {
this.props.addItemToCart(5);
}
public render() {
return (
<Grid centered>
<SecondComponent/>
</Grid>
);
}
}
const mapStateToProps = (state: RootState) => {
return {
userId: state.cart.userId,
selectedItems: state.cart.selectedItems
};
}
export default connect(
mapStateToProps,
{ addItemToCart, removeItemFromCart }
)(App);
3: The second component that doesn't have "mapStateToProps" invoked at all. :(
import * as React from 'react';
import { RootState } from '../store';
import { connect } from 'react-redux';
import { addItemToInventory, removeItemFromInventory } from '../store/inventory/actions';
import { Item, ItemTypesAvaliable } from '../store/inventory/types';
import { Fragment } from 'react';
import ItemTypeSection from './ItemTypeSection';
import { Grid } from 'semantic-ui-react';
export interface ISecondComponentProps{
removeItemFromInventory?: typeof removeItemFromInventory,
addItemToInventory?: typeof addItemToInventory,
items?: Item[],
itemTypesAvaliable?: ItemTypesAvaliable[]
}
export class SecondComponentextends React.Component<ISecondComponentProps> {
public render() {
let { itemTypesAvaliable } = this.props;
console.log(this.props);
return (
<Grid>
ok?
{itemTypesAvaliable != null ? itemTypesAvaliable.map(individualItemType => {
return <ItemTypeSection itemType={individualItemType} />
}) : <h1>doesn't work</h1>}
</Grid>
);
}
}
const mapStateToProps = (state: RootState) => {
console.log(state);
console.log("no???")
return {
// items: state.inventory.items,
// itemTypesAvaliable: state.inventory.itemTypesAvaliable
};
}
export default connect(
mapStateToProps,
{ addItemToInventory, removeItemFromInventory }
)(SecondComponent);```
Please make sure that you are importing the required instance. In your case, you need the redux connected instance of SecondComponent with the code
Right:
import SecondComponent from <pathToSecondComponentFile>
which is generating through
export default connect(
mapStateToProps,
{ addItemToInventory, removeItemFromInventory }
)(SecondComponent);
if you are importing the component like this
Wrong:
import {SecondComponent} from "<pathToSecondComponentFile>"
then please remove the braces of SecondComponent to get the default instance of export from file.
This is my app.js
import React from 'react'
import { Provider } from 'react-redux'
import { View } from 'react-native'
import { createStore, applyMiddleware } from 'redux'
import ReduxThunk from 'redux-thunk'
import Reducers from './redux'
import Routes from './config/routes'
import { ApolloClient, HttpLink, InMemoryCache } from 'apollo-boost'
import { ApolloProvider } from 'react-apollo'
const cache = new InMemoryCache();
const client = new ApolloClient({
cache,
link: new HttpLink({
uri: '...',
}),
})
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
initialized: true
}
}
render() {
return (
<View style={{ flex: 1 }}>
<ApolloProvider client={client}>
<Provider store={store}>
<Routes />
</Provider>
</ApolloProvider>
</View>
)
}
}
const store = createStore(Reducers, {}, applyMiddleware(ReduxThunk))
export default App
Ok, so far...basic.
This will render the initial file of my route: welcome.js
import React from 'react'
import {...} from 'react-native'
import { Actions } from 'react-native-router-flux'
import style from './style'
import { connect } from "react-redux"
import gql from 'graphql-tag'
class Welcome extends React.Component {
constructor(props) {
const LOGIN_MUTATION = gql`
mutation {
login(
email:"test#test.com"
password:"1234"
) {token}
}
`
// Bellow will not work..I've no idea how to call the client that
// I set at <ApolloProvider client={client}>
client
.mutate({
mutation: LOGIN_MUTATION
})
.then(res => console.log(res))
.catch(err => console.log(err))
}
}
const mapStateToProps = state => (
{
}
)
export default connect(mapStateToProps,
{
})(Welcome)
So, the client was defined on my app.js that's apply the provider and inject the routes.
I'd like to know how to be capable to execute the client defined at app,js into welcome.js
It's highly recommended that you switch for the new React Hooks version, using them, you can simply write const client = useApolloClient() to get access to your client instance:
import { useApolloClient } from '#apollo/react-hooks';
const Welcome = () => {
const client = useApolloClient();
return <h1>Welcome</h1>;
}
And regarding the ApolloProvider, it is configured in the same manner was you did, except that you can import it directly from the hooks package too, i.e import { ApolloProvider } from '#apollo/react-hooks -- and you can remove the react-apollo therefore.
See more details about hooks here: https://www.apollographql.com/docs/react/hooks-migration/.
But in case you really want to stay using class components, you can do:
import { getApolloContext } from 'react-apollo';
class Welcome extends React.Component {
...
}
Welcome.contextType = getApolloContext();
And then you'll be able to access the client using this.context.client inside your class:
class Welcome extends React.Component {
render() {
console.log('client', this.context.client);
return ...;
}
}
Welcome.contextType = getApolloContext();
You can use ApolloConsumer in your component to get access to the client:
To access the client directly, create an ApolloConsumer component and provide a render prop function as its child. The render prop function will be called with your ApolloClient instance as its only argument.
e.g.
import React from 'react';
import { ApolloConsumer } from "react-apollo";
const WithApolloClient = () => (
<ApolloConsumer>
{client => "We have access to the client!" /* do stuff here */}
</ApolloConsumer>
);
I checked the state of redux store is changed, but mapStateToProps is not working.
Here is my file tree and code. What I want is that when I get the data from REST API by axios then I dispatches the data to redux-store and update the state of redux-store. After this, I finally get the state of redux-store as like this.props.data
index.js
MainPage.js
- PageHeader.js
- AlarmContentList.js
MainPage.js
import React, {Component} from 'react'
import {connect, Provider} from 'react-redux'
import store from '../../store'
import ReactDOM from 'react-dom'
import axios from 'axios'
import * as AlarmContentActions from '../../store/modules/AlarmContent'
import PageHeader from './PageHeader'
class MainPage extends Component {
constructor(props) {
super(props)
axios.get("http://127.0.0.1:8000/api/main")
.then(res => {
this.setState({
AnswerPostMultiList: res.data,
})
}
)
.catch(err => {
console.log(err)
})
}
state = {
AnswerPostMultiList : [
],
}
render() {
this.props._getInBox(this.state.AnswerPostMultiList)
return(
<>
<PageHeader />
</>
)
}
}
MainPage.defaultProps = {
}
const mapDispatchToProps = (dispatch) => ({
_getInBox: (inBoxList) => {
dispatch(AlarmContentActions.inBox(inBoxList))
}
})
export default connect(null, mapDispatchToProps)(MainPage)
PageHeader.js
import React, {Component} from 'react'
import ReactDOM from 'react-dom'
import './PageHeader.css'
import AlarmContentList from './AlarmContent/AlarmConentList'
class PageHeader extends Component {
render() {
return (
<>
<AlarmContentList />
</>
)
}
}
export default PageHeader
AlarmContentList.js
import React, {Component} from 'react'
import {connect} from 'react-redux'
class AlarmContentList extends Component {
render() {
console.log(this.props, 'this.props')
return (
<>
<div>{this.props.inBox}<div>
</>
)
}
}
AlarmContentList.defaultProps = {
inBox: []
}
const mapStateToProps = (state) => ({
inBox : state.inBox
})
export default connect(mapStateToProps)(AlarmContentList);
index.js
import React, {Component} from 'react'
import {connect, Provider} from 'react-redux'
import store from '../../store'
import ReactDOM from 'react-dom'
import MainPage from './MainPage'
ReactDOM.render(
<Provider store={store}>
<MainPage />
</Provider>, document.getElementById('main-page'))
AlarmContent.js (redux-store)
import {createAction, handleActions} from 'redux-actions'
//define action types
const INBOX = 'AlarmContent/INBOX';
//CREATE action
export const inBox = createAction(INBOX);
// SET initialState
const initialState = {
inBox:[
]
}
// CREATE reducers
export default handleActions({
[INBOX] : (state, action) => {
return {
...state,
inBox : action.payload
}
},
}, initialState)
By checking redux-dev-tool, the procedure that I dispatch data and change state of redux store goes well, isn't it?
Even though state of redux-store is updated, why props from redux-store isn't changed??
(this result of image comes from console.log(this.props, 'this.props') at AlarmContentList.js)
Invariant Violation: Could not find "store" in either the context or props of "Connect(App)". Either wrap the root component in a , or explicitly pass "store" as a prop to "Connect(App)".
I hate to ask a variation of a question that has been asked many times, but I have tried all the proposed solutions with no luck.
https://codesandbox.io/s/0pyl7n315w
index.js
import React, {Component} from 'react'
import {AppRegistry} from 'react-native'
import {Provider} from 'react-redux'
import App from './app'
import configureStore from './store.js'
const store = configureStore();
class MyCounterApp extends Component {
render() {
return(
<Provider store={store}>
<App/>
</Provider>
)
}
}
AppRegistry.registerComponent('MyCounterApp', () => MyCounterApp)
app.js
import React from 'react';
import {Button, Text, View} from 'react-native';
import {addToCounter} from "./actions";
import { connect } from 'react-redux';
class App extends React.Component {
handleOnClick = event => {
this.props.addToCounter()
};
render() {
return (
<View>
<Text>{this.props.count}</Text>
<Button onPress={() => this.props.addToCounter()}
title={"Click Me!"}>
</Button>
</View>
)
}
}
function mapDispatchToProps(dispatch) {
return {
addToCounter: () => dispatch(addToCounter())
}
}
function mapStateToProps(state) {
return {
count: state.count
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
store.js
import reducer from './reducer'
import {createStore} from 'redux'
export default function configureStore() {
let store = createStore(
reducer
)
return store
}
reducer.js
import {ADD_TO_COUNTER} from './actions'
const initialState = {
counter: 0
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TO_COUNTER:
return {
...state,
counter: state.counter + 1
}
default:
return state
}
}
export default reducer;
I am following along with this tutorial:
https://medium.com/#pavsidhu/using-redux-with-react-native-9d07381507fe
If you want to test components without using store, the first thing you have to do is export your disconnected component like: export { Component }.
Then you'll have to import on your test file like that: import { Component } from ...
If the error persists, look to see if the component uses components that are connected, and if yes, you have to mock that one, for example:
jest.mock('../../../AnotherComponent', () => ChildComponent => props => <ChildComponent {...props} />);
Example:
const Component = (props: Props) => (
<>
...
<AnotherComponent // connected component inside <Component />
{...props}
/>
</>
)
const mapStateToProps = (state: State) => ({});
const mapDispatchToProps = {
export default connect(
mapStateToProps,
mapDispatchToProps
)(Component);
export { Component }; // disconnected component
You have not provided store to your App component. that's why it is failed to connect component with reducer:
class MyCounterApp extends Component {
render() {
return(
<Provider store={store}>
<App/>
</Provider>
)
}
}
Remove provider from app.js
please help.
this.props.store does not work on child components.
but the connect(mapStateToProps, mapDispatchToProps) ... is working fine.
why doesn't work only child components?
1. parent code (is working fine)
import React from 'react';
import ReactDOM from 'react-dom';
import { Home } from './container/home/index';
import { ChildrenComponent } from './container/childrenComponent';
import { Match, Miss } from 'react-router';
import { BrowserRouter as Router } from 'react-router';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import Reducers from './reducers';
const store = createStore(Reducers);
store.subscribe(() => console.log('ㅡㅡㅡㅡㅡ store was updated ㅡㅡㅡㅡㅡ'));
store.subscribe(() => console.log(store.getState()));
store.subscribe(() => console.log('ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ'));
ReactDOM.render(
<Provider store={store}>
<Router>
<div>
<Match pattern="/" component={Home} />
<Match pattern="/ChildrenComponent" component={ChildrenComponent} />
</div>
</Router>
</Provider>,
document.getElementById('root')
);
2. Children Component (is not working only 'this.props.store ..... ')
import React, { Component } from 'react';
import $ from 'jquery';
import { connect } from 'react-redux';
class ChildrenComponent extends Component {
constructor (props) {
super (props);
}
render (
console.log(this.props.store) // undfined
console.log(this.props.store.getState()) // does not working
const mapStateToProps = (state) => {
return {
// .... is working fine
}
}
const mapDispatchToProps = (dispatch) => {
return {
// .... is working fine
}
}
)
return (
<divHellow world</div>
)
}
expoert default connect(mapStateToProps, mapDispatchToProps)(ChildrenComponent);
Trying to access the store and its state directly defeats the entire purpose of using Redux, and React-Redux in particular. You're supposed to use mapStateToProps to access parts of state that you need in each particular component.
Put mapStateToProps and mapDispatchToProps outside the component class and put your jsx inside the render method of your component class.
class ChildrenComponent extends Component {
render () {
return (
<divHellow world</div>
)
}
}
const mapStateToProps = (state) => {
return {
// ....
}
}
const mapDispatchToProps = (dispatch) => {
return {
// ....
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ChildrenComponent);