I am unable to use the #connect() syntax in place of export default connect()
I've noticed that when I use the usual syntax
class PhonePage extends Component { ... }
export default connect(state => ({
testedByPhone: state.phonepage.testedByPhone
}),
(dispatch) => ({
actions: bindActionCreators(testUserPhone, dispatch)
})
)(PhonePage)
I get my state properly registered in my store.
And it looks like this: Object {screenProps: undefined, navigation: Object, testedByPhone: Object}
But when I use the #connect decorator to make things cleaner, I don't get anything listed in my state.
#connect(
state => ({
testedByPhone: state.phonepage.testedByPhone
}),
{ testUserPhone }
)
class PhonePage extends Component { ... }
export default PhonePage
Suddenly it's somehow not actually connecting things: Object {screenProps: undefined, navigation: Object}
What am I doing wrong, and what is the correct way to use the magical #connect decorator that I see everyone using?
The rest of the code, just in case it's needed, in the form of #connect;
'use strict'
import React, { Component } from 'react'
import {
AppRegistry,
StyleSheet,
Text,
ScrollView,
View,
StatusBar,
TouchableHighlight,
Button,
TextInput
} from 'react-native'
import { NavigationActions } from 'react-navigation'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { testUserPhone } from './actions'
import PhoneInput from 'react-native-phone-input'
#connect(
state => ({
testedByPhone: state.phonepage.testedByPhone
}),
{ testUserPhone }
)
class PhonePage extends Component {
constructor(){
super()
}
componentDidMount() {
// this.props.testUserPhone
}
render() {
console.log(this.props)
return(
...
)
}
}
export default PhonePage
// export default connect(state => ({
// testedByPhone: state.phonepage.testedByPhone
// }),
// (dispatch) => ({
// actions: bindActionCreators(testUserPhone, dispatch)
// })
// )(PhonePage)
// module.exports = PhonePage
The #connect decorator, or decorators in general, are not native to JavaScript. However, you can add them in via Babel.
You can read more on it here
npm install --save-dev babel-plugin-transform-decorators-legacy
For babel-6, follow #megaboy101's answer
For babel-7 use this
{
"plugins": [
["#babel/plugin-proposal-decorators", { "legacy": true }],
]
}
Related
I am new to using redux for React Native and am testing it with a simple case. I have been able to successfully connect to the store, and I can see the action is dispatched properly using the redux debugger, however, the store is not updating in the debugger. I've tried several different implementations, but nothing is working. Any help would be appreciated!
Component:
import React, { PureComponent } from 'react'
import { Text, TouchableOpacity, SafeAreaView, Alert, Button } from 'react-native'
import { Navigation } from 'react-native-navigation';
import { connect } from 'react-redux'
import simpleAction from '../store/actions/simpleAction'
class App2 extends PureComponent {
constructor(props){
super(props);
}
pressRedux = () => {
const data = 'hello'
this.props.simpleAction(data)
}
render() {
return (
<SafeAreaView>
<Text>
{this.props.state.simpleReducer.text}
</Text>
<Button onPress = {this.pressRedux} title = 'Redux' />
</SafeAreaView>
)
}
}
function mapStateToProps(state) {
return {
state: state
};
}
const mapDispatchToProps = {
simpleAction
}
export default connect(mapStateToProps, mapDispatchToProps)(App2);
Action:
import {SET_TEXT} from '../types/types'
export default function simpleAction(data) {
return({
type: SET_TEXT,
payload: data
})
}
reducer:
import SET_TEXT from '../types/types'
const INITIAL_STATE = {
text: 'Hi'
}
const simpleReducer = (state = INITIAL_STATE, action ) => {
switch(action.type){
case SET_TEXT:
return { ...state, text: action.payload};
default:
return state;
}
}
export default simpleReducer;
The code you've shared here looks correct. Only thing I can suggest is, if you're seeing the action come through in the debugger, your issue is either with the data/payload or logic within simpleReducer.
In this case you have it properly stripped down so I'd almost think this isn't actually the code you are running, it might be something in your build process?
I am getting this error:
./src/components/Playing.jsx
Line 15: 'aaa' is not defined no-undef
in my Playing.jsx:
import React, { Component } from 'react'
console.log(aaa);
From my Token.jsx
import { connect } from 'react-redux'
imports { Playing } from '../components/Playing'
const mapStateToProps = state => ({
aaa: "asdf"
})
export default connect(mapStateToProps)(Playing)
You won't be able to just console.log() anywhere in the file; it would need to be within some function of the Playing component; and it will also only be available via props, e.g.
class Playing extends React.Component {
componentDidMount() {
console.log(this.props.aaa);
}
render() {
return <span>Playing</span>;
}
}
I'm experimenting with vuex and I was looking for best way to organize my vuex files I finished with something like this:
/src/store/user/state.js:
export default {
state: {
user: null
}
}
/src/store/user/getters.js:
export default {
getters: {
user (state) {
return state.user
}
}
}
/src/store/user/mutations.js:
export default {
mutations: {
'SET_USER' (state, user) {
state.user = user
}
}
}
/src/store/user/actions.js
export default {
actions: {
loginUser ({ commit }, params) {
commit('SET_USER', {id: 1})
}
}
}
/src/store/user/index.js
import state from './state'
import getters from './getters'
import actions from './actions'
import mutations from './mutations'
export default {
state,
getters,
actions,
mutations
}
/src/store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'
import user from './user'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
user
}
})
When I load my code it returns in console following error:
vuex: unknown getter: user
Each of your user-related files are using export default, which means when you import those files, you are naming the entire object being exported state, getters, etc.
So, in the scope of index, the state variable has a property named state, the getters variable has a property named getters, etc. And this is throwing things off.
You should export a const for each of these files instead:
export const state = {
user: null,
}
And then when importing grab the named const like so:
import { state } from './state'
Alternatively, you could just remove the properties for state, getters, etc. from each file:
// state.js
export default {
user: null,
}
And then import like you're doing already:
import state from './state'
I'm testing some react components, a basic tests suite just to know if a component is rendering and their childs.
I'm using redux-mock-store to make the store and {mount} enzyme to mount the container in a provider, but even mocking the correct store this error is always fired:
TypeError: Cannot read property 'pathname' of undefined
Here is my very deadly basic test:
import React from 'react';
import { mount } from 'enzyme';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import App from '../containers/App.container';
describe('App', () => {
let wrapper;
const mockStore = configureStore([]);
const store = mockStore({
router: {
location: { pathname: '/home', query: {}, search: '' },
params: {}
}
});
console.log(store.getState());
beforeEach(() => {
wrapper = mount(
<Provider store={store}>
<App />
</Provider>
);
});
it('Should render app and container elements', () => {
expect(wrapper.find('.app').exists()).toBeTruthy();
expect(wrapper.find('.container').exists()).toBeTruthy();
});
it('Should render the navbar', () => {
expect(wrapper.find('nav').exists()).toBeTruthy();
});
});
And the (even more) simple component / container:
import React, { Component } from 'react';
import NavBar from '../components/Navbar';
class App extends Component {
render() {
const { location, logout} = this.props;
console.log(location);
return (
<section className='app'>
<NavBar location={location.pathname} onLogoutClick={logout}/>
<div className='container'>
{this.props.children}
</div>
</section>
);
}
}
export default App;
Container:
import { connect } from 'react-redux';
import { signOut } from '../actions/auth.actions'
import App from '../components/App';
const mapStateToProps = (state, ownProps) => {
return {
location: ownProps.location
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
logout: () => {
dispatch(signOut())
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
I can't figure out the problem of the test, the mockStore is in the correct format:
Any idea?
Update:
Thinking about it, I have no reducer / prop in the rootReducer for the location, but, i just want to pass down through the children components the location object properties that react-redux-router make available in the ownProps argument.
Weird fact: logging the location property in the app returns me the correct object.
In the tests, is always undefined... (as the error shows).
Here is my rootReducer:
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import { routerReducer } from 'react-router-redux';
import authReducer from './auth.reducer';
import analysisReportsReducer from './AnalysisReports.reducer';
import titleAnalysisReducer from './TitleAnalysis.reducer';
import postsReportsReducer from './PostsReports.reducer';
const rootReducer = combineReducers({
form: formReducer,
routing: routerReducer,
auth: authReducer,
analysis: titleAnalysisReducer,
analysis_reports: analysisReportsReducer,
posts: postsReportsReducer
});
export default rootReducer;
It looks like your location object is scoped beneath the router.
Your test may be grabbing the window.location property, which your test suite may not replicate, assuming the test is cli and not in a browser.
Perhaps try:
<NavBar location={this.props.router.location.pathname} onLogoutClick={logout}/>
I'm new to redux and having trouble wrapping my head around presentational and container components.
Relevant stack:
react v0.14.8
react-native v0.24.1
redux v3.5.2
react-redux v4.4.5
The issue:
I have a login button component, which when rendered checks the login status and calls the onSuccessfulLogin action which updates the state with the user's Facebook credentials.
However, when trying to separate this into separate presentational/container components, I'm unable to call the onSuccessfulLogin action: Error: onSuccessfulLogin is not defined.
What am I doing wrong here? I'd imagine there's something simple that I'm not understanding with the relationship between the two components and the connect() function.
Presentational Component (Login.js)
import React, { PropTypes } from "react-native";
import FBLogin from "react-native-facebook-login";
import UserActions from "../users/UserActions";
class LoginPage extends React.Component {
render() {
const { userData, onSuccessfulLogin } = this.props;
return (
<FBLogin
permissions={["email","user_friends"]}
onLoginFound= { data => {
onSuccessfulLogin(data.credentials);
}}
/>
)
}
};
export default LoginPage;
Container Component (LoginContainer.js)
import { connect } from 'react-redux';
import LoginPage from "../login/LoginPage";
import UserActions from "../users/UserActions";
const mapDispatchToProps = (dispatch) => {
return {
onSuccessfulLogin: (userData) => {
dispatch(UserActions.userLoggedIn(userData))
}
}
}
const mapStateToProps = (state) => {
return {
userData: state.userData
}
}
const LoginContainer = connect(
mapStateToProps,
mapDispatchToProps
)(LoginPage);
export default LoginContainer;
Also, if I wanted to make the updated state.userData accessible to the LoginPage component, how would I do that? Any help is appreciated!
Solved! When using ES6 classes, you're required to call super(props) in a constructor method in order to access the container's properties in the connected presentational component:
class LoginPage extends React.Component {
constructor(props){
super(props);
}
render(){
// ...
}
}
Your container component is supposed to be a component and it must have a render function with the dumb/presentational components you want to render.
import { connect } from 'react-redux';
import LoginPage from "../login/LoginPage";
import UserActions from "../users/UserActions";
class LoginContainer extends React.Component {
constructor(props){
super(props);
}
render() {
return (
<LoginPage userData={this.props.userData}
onSuccessfulLogin={this.props.onSuccessfulLogin}
/>
)
}
};
const mapDispatchToProps = (dispatch) => {
return {
onSuccessfulLogin: (userData) => {
dispatch(UserActions.userLoggedIn(userData))
}
}
}
const mapStateToProps = (state) => {
return {
userData: state.userData
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(LoginPage);