I'm trying to write js code transformer. I need to parse JS into an AST do some modifications, for example add a new import declaration, and generate JS code back.
Currently I have some troubles with generating JS code. Decorators appear in a wrong place and generator remove brackets around JSX.
I'm new in this area, so probably, I miss some options while transforming/generating code.
Source code:
// Core
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actions } from '../../actions/navigation';
const mapStateToProps = ({ navigation }) => ({ // eslint-disable-line arrow-body-style
menuStatus: navigation.get('menuStatus')
});
const mapDispatchToProps = (dispatch) => ({ // eslint-disable-line arrow-body-style
actions: bindActionCreators({ ...actions }, dispatch)
});
#connect(mapStateToProps, mapDispatchToProps)
export default class Home extends Component {
render () {
return (
<section>
<h1>Home container!</h1>
</section>
);
}
}
Parsed/generated code:
// Core
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actions } from '../../actions/navigation';
const mapStateToProps = ({ navigation }) => ({ // eslint-disable-line arrow-body-style
menuStatus: navigation.get('menuStatus')
});
const mapDispatchToProps = dispatch => ({ // eslint-disable-line arrow-body-style
actions: bindActionCreators({ ...actions }, dispatch)
});
export default #connect(mapStateToProps, mapDispatchToProps)
class Home extends Component {
render() {
return <section>
<h1>Home container!</h1>
</section>;
}
}
Demo repository
It is a Babel bug: https://github.com/babel/babel/issues/4585
If the expectation is that you want to generate code that could be saved back to the filesystem, you are likely better off looking at JSCodeShift since it is targetted at preserving formatting while manipulating code, while Babel has only some interest in existing formatting.
Related
I am trying to implement Redux in a Next.js app and have problems getting the dispatch function to work in getInitialProps. The store is returned as undefined for some reason that I cannot figure out. I am using next-redux-wrapper. I have followed the documentation on next-redux-wrapper GitHub page but somewhere on the way it goes wrong. I know the code is working - I used axios to directly fetch the artPieces and then it worked just fine but I want to use Redux instead. I am changing an react/express.js app to a Next.js app where I will use the API for the basic server operations needed. This is just a small blog app.
Here is my store.js:
import { createStore } from 'redux';
import { createWrapper, HYDRATE } from 'next-redux-wrapper';
// create your reducer
const reducer = (state = { tick: 'init' }, action) => {
switch (action.type) {
case HYDRATE:
return { ...state, ...action.payload };
case 'TICK':
return { ...state, tick: action.payload };
default:
return state;
}
};
// create a makeStore function
const makeStore = (context) => createStore(reducer);
// export an assembled wrapper
export const wrapper = createWrapper(makeStore, { debug: true });
And here is the _app.js:
import './styles/globals.css';
import { wrapper } from '../store';
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
export default wrapper.withRedux(MyApp);
And finally here is where it does not work. Trying to call dispatch on the context to a sub component to _app.js:
import React from 'react';
import { ArtPiecesContainer } from './../components/ArtPiecesContainer';
import { useDispatch } from 'react-redux';
import axios from 'axios';
import { getArtPieces } from '../reducers';
const Art = ({ data, error }) => {
return (
<>
<ArtPiecesContainer artPieces={data} />
</>
);
};
export default Art;
Art.getInitialProps = async ({ ctx }) => {
await ctx.dispatch(getArtPieces());
console.log('DATA FROM GETARTPIECES', data);
return { data: ctx.getState() };
};
This should probably work with "next-redux-wrapper": "^7.0.5"
_app.js
import { wrapper } from '../store'
import React from 'react';
import App from 'next/app';
class MyApp extends App {
static getInitialProps = wrapper.getInitialAppProps(store => async ({Component, ctx}) => {
return {
pageProps: {
// Call page-level getInitialProps
// DON'T FORGET TO PROVIDE STORE TO PAGE
...(Component.getInitialProps ? await Component.getInitialProps({...ctx, store}) : {}),
// Some custom thing for all pages
pathname: ctx.pathname,
},
};
});
render() {
const {Component, pageProps} = this.props;
return (
<Component {...pageProps} />
);
}
}
export default wrapper.withRedux(MyApp);
and Index.js
import { useEffect } from 'react'
import { useDispatch } from 'react-redux'
import { END } from 'redux-saga'
import { wrapper } from '../store'
import { loadData, startClock, tickClock } from '../actions'
import Page from '../components/page'
const Index = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(startClock())
}, [dispatch])
return <Page title="Index Page" linkTo="/other" NavigateTo="Other Page" />
}
Index.getInitialProps = wrapper.getInitialPageProps(store => async (props) => {
store.dispatch(tickClock(false))
if (!store.getState().placeholderData) {
store.dispatch(loadData())
store.dispatch(END)
}
await store.sagaTask.toPromise()
});
export default Index
For the rest of the code you can refer to nextjs/examples/with-redux-saga, but now that I'm posting this answer they're using the older version on next-redux-wrapper ( version 6 ).
I have been reading several documents and watching videos regarding React Redux, but since all of them are different I wasn't able to apply that knowledge to some real project.
I will try to enumarate the process in order to use React Redux together.
Directory Structuring
project
src
components
User
index.js (Container component)
page.js (Presentational component)
actions
users.js
index.js (exports actionCreators combination)
reducers
users.js
index.js (exports reducer combination with combineReducers
constants
actionTypes.js
services
users.js
index.js
store.js
public
index.html
Redux Setup
We create constants in project/src/constants/actionTypes.js:
export const CREATE_USER = 'CREATE_USER';
export const DELETE_USER = 'DELETE_USER';
export const UPDATE_USER = 'UPDATE_USER';
We create actionCreators en project/src/actions/users.js y luego se combinan en project/src/actions/index.js:
users.js
import { CREATE_USER } from '../constants/actionTypes';
export default function createUser(user) {
type: CREATE_USER,
user
}
index.js
import { createUser } from './users';
export default {
createUser
}
We create reducers in project/src/reducers/users.js and they are combined in project/src/reducers/index.js using combineReducers():
users.js
import { CREATE_USER, UPDATE_USER, DELETE_USER } from '../constants/actionTypes';
import { createUser } from '../services/users';
const initialState = {
name: '',
password: '',
email: ''
}
export default function users(state = initialState, action) {
switch (action.type) {
case CREATE_USER:
state = createUser(action.user);
return state;
}
}
index.js
import users from './users';
export default combineReducers({
users
})
We create store in project/src/store.js:
import { createStore } from 'redux';
import reducers from './reducers';
export const store = createStore(reducers);
React Redux Setup
We wrap component application <Provider> in project/src/index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './store';
const Root = () => (
`
<Provider store={store}>
<App />
</Provider>
`
)
ReactDOM.render(Root, document.getElementById('root');
We transform component state to properties with mapStateToProps in project/src/components/User/index.js:
import React, { Component } from 'react';
import { createUser } from '../../actions/users';
import Page from './page';
class User extends Component {
render() {
return <Page users={this.props.users} />
}
}
const mapStateToProps = state => ({
users: this.props.users
// what is mapped here?
});
const mapDispatchToProops = dispatch => ({
// what about here?
});
export default connect(mapStateToProps, mapDispatchToProps)(User);
So, the question would be, is this React-Redux cycle well formed? What is missing or wrong?
Yes, the folder structure works well. As for the "fetch" or "service" functionality you're talking about, I'll give you an example of what actions and reducers both should, in a basic example, do.
So if you're working with a backend which you're "fetching" anything from, I'd recommend adding that functionality in the action, not the reducer:
import { USERS_FETCHED } from '../constants/actionTypes';
import { baseUrl } from "../constants/baseUrl";
const usersFetched = users => ( { // action to dispatch
type: USERS_FETCHED,
users,
} );
export const fetchUsers = () => ( dispatch ) => { // export for mapDispatchToProps
request( `${ baseUrl }/users` )
.then( response => {
dispatch( usersFetched( response.body ) ); // dispatch the action to reducer
} )
.catch( console.error );
}; // in your case you import createUser(), but it works either way
Now the action is concerned with functionality, in contrast the reducer is only concerned with managing the Redux state:
import { USERS_FETCHED } from "../constants/actionTypes";
export default ( state = null, action = {} ) => {
switch ( action.type ) {
case USERS_FETCHED:
return action.users;
default:
return state;
}
};
Functionality in the reducer is fine, but it should only be concerned with managing state. You can imagine how cluttered the code could get if you start fetching any data here, not to mention problems with asynchronicity. Of course, this is just one way to do it, but it works solidly. Hope this helps you in some way.
I'm writing a mobile app with React Native. I have two js files as following:
Error.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getTranslate } from 'react-localize-redux';
function mapStateToProps(state) {
return {
t: getTranslate(state.locale)
};
}
export default connect(
mapStateToProps
)(
({ t }) => ({
e001: t('wrong_format'),
e002: t('invalid_email'),
})
);
SignIn.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getTranslate } from 'react-localize-redux';
import { Field, reduxForm } from 'redux-form';
import Error from './Error';
const validate = (values) => {
console.log('error: ', Error);
// Process validate redux-form with messages from Error.js
};
class SignIn extends Component {
// Process login form with redux-form
}
function mapStateToProps(state) {
return {
t: getTranslate(state.locale),
};
}
const SignInForm = {
form: 'SignIn',
validate,
};
export default connect(
mapStateToProps
)(
reduxForm(SignInForm)(
SignIn
)
);
How can I use the data that exported from Error.js in SignIn.js? (e.g. values of 'e001', 'e002')
Example from validate function (in SignIn.js) I wanna show the value of code "e001" from Error.js.
For more detail, my idea is collect all error messages from language file (using react-localize-redux) into Error.js, then from validate functions of redux-form, i can show those messages easier.
Its look like you want Error.js as a helper file.But you are implemented it as react-redux container.
If you implementing it as react-redux container then necessarily it will call every time whenever store will change.
Instead simply export it.
import { getTranslate } from 'react-localize-redux';
const format = ({
e001: getTranslate('wrong_format'),
e002: getTranslate('invalid_email'),
})
export default format;
Called it as once imported
console.log(format.e001);
Currently, I'm working on a small application that utilizes modals. I don't want to use 'ready-to-use' packages like react-modal and instead decided to try to do it on my own.
1) A reducer in src/reducers/modalReducer.js
const modalReducer = (state = {
show: true,
}, action) => {
switch (action.type) {
case 'TOGGLE_MODAL':
console.log('reducer worked out')
state = {...state, show: !state.show }
break
default:
return state
}
}
export default modalReducer
My reducers/index.js
import { combineReducers } from 'redux'
import modalReducer from './modalReducer'
const reducers = combineReducers({
modal: modalReducer
})
export default reducers
2) A store in src/store.js
import { createStore } from 'redux'
import reducer from './reducers/index'
export default createStore(reducer)
3) A Modal component in src/components/Modal.js. I want this component to be reusable and contain input forms which I'll add later.
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { toggleModal } from '../actions/index'
import { bindActionCreators } from 'redux'
import '../css/Modal.css'
class Modal extends Component {
render () {
if(!this.props.show) {
return (<h1>FUC YOU</h1>)
}
console.log('HELLLO' + this.props.show)
return (
<div className='backdrop'>
<div className='my-modal'>
<div className='footer'>
<button className='close-btn' onClick={ () => toggleModal }>
X
</button>
</div>
<h1>{ this.props.title }</h1>
<hr/>
<div>
{ this.props.contents }
</div>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return { show: state.modal.show }
}
const mapDispatchToProps = (dispatch) => {
return {
toggleModal: () => dispatch(toggleModal())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Modal)
My problem is that when I'm pressing the button x, in my modal nothing happens. It means that I did something wrong when was dispatching actions, but I have no idea what I missed...
At this point I just want my empty modal to be closed when the x button is pressed.
In my index.js I have the following structure:
import React from 'react'
import ReactDOM from 'react-dom'
import registerServiceWorker from './registerServiceWorker'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import store from './store.js'
import App from './components/App'
ReactDOM.render(
<Provider store = {store} >
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root'))
registerServiceWorker()
My Modal component is within App
You're not actually calling the toggleModal() action creator. In addition, you're referencing the imported function, not the function you're getting as props:
onClick={ () => toggleModal }
The immediate fix would be: onClick={ () => this.props.toggleModal() }.
Having said that, there's two other ways you can improve this code.
First, you can pass toggleModal directly as the handler for onClick, like:
onClick={this.props.toggleModal}
Second, you can replace the mapDispatch function by using the "object shorthand" syntax supported by connect:
import {toggleModal} from "../actions";
const actions = {toggleModal};
export default connect(mapState, actions)(Modal);
Beyond that, I'd encourage you to read my post Practical Redux, Part 10: Managing Modals and Context Menus, which specifically shows how to implement modal dialogs using React and Redux, and points to additional resources on the topic.
Problem:
I can't display the value from the state of redux, which is delivered by mapStateToProps function to the component.
Project structure:
Create-react-app CLi application built the project.
Inside of the src/ I have the following code structure
Necessary code:
The main page which we are interacting with looks like this:
Underneath it is planned to post the result of the clicking on the buttons.
So how do I bind the redux state and actions to those two components: Calculator and ResultLine?
Let me show the index.js code, where I create the store:
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducers from './reducers/';
import App from './components/App';
ReactDOM.render(
<Provider store={createStore(reducers)}>
<App />
</Provider>,
document.getElementById("root")
);
There are only three actions:
import {CALCULATE, ERASE, PUT_SYMBOL} from "./types";
export const putSymbol = (symbol) => {
return {
type: PUT_SYMBOL,
payload: symbol
}
};
export const calculate = () => {
return {
type: CALCULATE
}
};
export const erase = () => {
return {
type: ERASE
}
};
And in the App.js I pass reducers, which are binded to those actions to the Calculator component:
import React, {Component} from 'react';
import Calculator from './Calculator';
import ResultLine from "./ResultLine";
import {calculate, erase, putSymbol} from "../actions/index";
import {connect} from "react-redux";
class App extends Component {
render() {
return (
<div>
<Calculator
onSymbolClick={this.props.onSymbolClick}
onEqualsClick={this.props.onEqualsClick}
onEraseClick={this.props.onEraseClick}/>
<br/>
<ResultLine result={this.props.result}/>
</div>
);
}
}
const mapStateToProps = (state) => {
console.log('mapState', state.calc.line);
return {
result: state.line
}
};
const mapDispatchToProps = {
onSymbolClick: putSymbol,
onEqualsClick: calculate,
onEraseClick: erase
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
And that works fine. Whenever I click the button the state changes, and I observe it in the console log, called in mapStateToProps function.
So I expect, that I can deliver result prop to the Result line easily, and I pass it into the ResultLine component as a parameter. So, let's look at that element:
import React from 'react';
const ResultLine = ({result}) => {
return (
<p>{result}</p>
);
};
export default ResultLine;
And I can see no changes in a result line. Maybe, something wrong with the React/Redux lifecycle management and ResultLine component just does not update on changes in state?
There's an error on mapStateToProps.
Instead of:
const mapStateToProps = (state) => {
return {
result: state.line
}
}
Please use:
const mapStateToProps = (state) => {
return {
result: state.calc.line // calc was missing here
}
}