Igor Suvorov
Программист-предприниматель
Занятие 7
1 Apr 2017
Профессия
Node.js & React.js developer
продвинутый курс
const lib = require('some-lib');
lib.show();
/models
/views
/controllers
/components
/containers
src
├── containers
│ └── AppContainer.js
├── data
│ ├── TodoActions.js
│ ├── TodoActionTypes.js
│ ├── TodoDispatcher.js
│ └── TodoStore.js
├── root.js
└── views
└── AppView.js
TodoDispatcher.js
import {Dispatcher} from 'flux';
export default new Dispatcher();
TodoActionTypes.js
const ActionTypes = {
ADD_TODO: 'ADD_TODO',
};
export default ActionTypes;
TodoActions.js
import TodoActionTypes from './TodoActionTypes';
import TodoDispatcher from './TodoDispatcher';
const Actions = {
addTodo(text) {
TodoDispatcher.dispatch({
type: TodoActionTypes.ADD_TODO,
text,
});
},
};
export default Actions;
TodoStore.js
import Immutable from 'immutable';
import {ReduceStore} from 'flux/utils';
import TodoActionTypes from './TodoActionTypes';
import TodoDispatcher from './TodoDispatcher';
class TodoStore extends ReduceStore {
constructor() {
super(TodoDispatcher);
}
getInitialState() {
return Immutable.OrderedMap();
}
reduce(state, action) {
switch (action.type) {
case TodoActionTypes.ADD_TODO:
// Do nothing for now, we will add logic here soon!
return state;
default:
return state;
}
}
}
export default new TodoStore();
AppContainer.js
import AppView from '../views/AppView';
import {Container} from 'flux/utils';
import TodoStore from '../data/TodoStore';
function getStores() {
return [
TodoStore,
];
}
function getState() {
return {
todos: TodoStore.getState(),
};
}
export default Container.createFunctional(AppView, getStores, getState);
index.js
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
let store = createStore(todoApp)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
actions/index.js
let nextTodoId = 0
export const addTodo = (text) => {
return {
type: 'ADD_TODO',
id: nextTodoId++,
text
}
}
export const setVisibilityFilter = (filter) => {
return {
type: 'SET_VISIBILITY_FILTER',
filter
}
}
export const toggleTodo = (id) => {
return {
type: 'TOGGLE_TODO',
id
}
}
reducers/todos.js
const todo = (state = {}, action) => {
switch (action.type) {
case 'ADD_TODO':
return {
id: action.id,
text: action.text,
completed: false
}
case 'TOGGLE_TODO':
if (state.id !== action.id) {
return state
}
return Object.assign({}, state, {
completed: !state.completed
})
default:
return state
}
}
const todos = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
todo(undefined, action)
]
case 'TOGGLE_TODO':
return state.map(t =>
todo(t, action)
)
default:
return state
}
}
export default todos
reducers/visibilityFilter.js
const visibilityFilter = (state = 'SHOW_ALL', action) => {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter
default:
return state
}
}
export default visibilityFilter
Presentational Components
components/Todo.js
components/TodoList.js
components/Link.js
components/Footer.js
containers/VisibleTodoList.js
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_ALL': return todos
case 'SHOW_COMPLETED': return todos.filter(t => t.completed)
case 'SHOW_ACTIVE': return todos.filter(t => !t.completed)
}
}
containers/VisibleTodoList.js
...
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
export default VisibleTodoList
<Provider> & @inject
import {observable} from 'mobx';
var appState = observable({
timer: 0
});
appState.resetTimer = function reset() {
appState.timer = 0;
};
setInterval(function tick() {
appState.timer += 1;
}, 1000);
import {observer} from 'mobx-react';
@observer
class TimerView extends React.Component {
render() {
return (<button onClick={this.onReset.bind(this)}>
Seconds passed: {this.props.appState.timer}
</button>);
}
onReset () {
this.props.appState.resetTimer();
}
};
React.render(<TimerView appState={appState} />, document.body);
class Tea extends React.Component {
render() {
var {name, steepingTime} = this.props.tea;
return (
<li key={name}>
{name} (<em>{steepingTime} min</em>)
</li>
);
}
}
Tea = Relay.createContainer(Tea, {
fragments: {
tea: () => Relay.QL`
fragment on Tea {
name,
steepingTime,
}
`,
},
});
class TeaStore extends React.Component {
render() {
return <ul>
{this.props.store.teas.map(
tea => <Tea tea={tea} />
)}
</ul>;
}
}
TeaStore = Relay.createContainer(TeaStore, {
fragments: {
store: () => Relay.QL`
fragment on Store {
teas { ${Tea.getFragment('tea')} },
}
`,
},
});
class TeaHomeRoute extends Relay.Route {
static routeName = 'Home';
static queries = {
store: (Component) => Relay.QL`
query TeaStoreQuery {
store { ${Component.getFragment('store')} },
}
`,
};
}
ReactDOM.render(
<Relay.RootContainer
Component={TeaStore}
route={new TeaHomeRoute()}
/>,
mountNode
);
Restful?
Websockets?
Subscribers?
any questions?
программист-предприниматель
By Igor Suvorov
* template