Created
May 8, 2019 13:50
-
-
Save pasaran/021740a534ab4c251c99a122b237d529 to your computer and use it in GitHub Desktop.
Anti Redux
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const React = require( 'react' ); | |
const { connect } = require( './store' ); | |
class App extends React.Component { | |
componentDidMount() { | |
this.props.items.load_more(); | |
} | |
render() { | |
return ( | |
<div> | |
{ this.render_count() } | |
{ this.render_items() } | |
</div> | |
); | |
} | |
render_items() { | |
let more; | |
let loading; | |
// В каждом сторе могут быть и данные и экшены. | |
// И, скажем, селекторы могли бы быть. | |
// | |
// Тут свойство используется. | |
if ( this.props.items.loading ) { | |
loading = 'Loading...'; | |
} else { | |
// Тут экшен используется. | |
more = ( | |
<div onClick={ () => this.props.items.load_more() }>More...</div> | |
); | |
} | |
const items = this.props.items.items.map( item => { | |
return ( | |
<div key={ item }>{ item }</div> | |
); | |
} ); | |
return ( | |
<div> | |
{ items } | |
{ more } | |
{ loading } | |
</div> | |
); | |
} | |
render_count() { | |
return ( | |
<div> | |
{ this.props.count.count } | |
<div onClick={ () => this.props.count.increment() }>[ + ]</div> | |
<div onClick={ () => this.props.count.decrement() }>[ - ]</div> | |
</div> | |
); | |
} | |
} | |
// Компонент коннектится к конкретным сторам. | |
// Любое использование компонента притащит за собой все нужные сторы, экшены и редьюсеры. | |
// | |
module.exports = connect( { | |
items: require( './store-items' ), | |
count: require( './store-count' ), | |
}, App ); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<body> | |
<div id="root"></div> | |
<script src="build/index.js"></script> | |
</body> | |
</html> | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const React = require( 'react' ); | |
const ReactDOM = require( 'react-dom' ); | |
const App = require( './App' ); | |
const root = document.getElementById( 'root' ); | |
ReactDOM.render( <App/>, root ); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"dependencies": { | |
"react": "*", | |
"react-dom": "*" | |
}, | |
"devDependencies": { | |
"@babel/core": "*", | |
"@babel/preset-env": "*", | |
"@babel/preset-react": "*", | |
"babel-loader": "*", | |
"webpack": "*", | |
"webpack-cli": "*" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { create_store } = require( './store' ); | |
const initial_state = { | |
count: 0, | |
}; | |
module.exports = create_store( initial_state, { | |
// Это экшен, который сразу вызывает нужные редьюсер. | |
// Без всяких промежуточных объектов для экшена. | |
// Не нужно заводить action-type, не нужно потом его матчить внутри гигантского редьюсера. | |
// | |
increment( by = 1 ) { | |
// В this.update можно передать или новое значение стейта, | |
// или же редьюсер, в который передается стейт из которого нужно вернуть | |
// новое значение стейта. | |
// | |
this.update( ( state ) => ( { ...state, count: state.count + by } ) ); | |
}, | |
decrement( by = 1 ) { | |
this.update( ( state ) => ( { ...state, count: state.count - by } ) ); | |
}, | |
} ); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { create_store } = require( './store' ); | |
const initial_state = { | |
loading: false, | |
items: [], | |
}; | |
module.exports = create_store( initial_state, { | |
load_more() { | |
// Тут не нужно заводить отдельный action-type, | |
// чтобы только поменять значение флага loading. | |
// | |
this.update( ( state ) => ( { ...state, loading: true } ) ); | |
// Типа асинхронный экшен. | |
// | |
setTimeout( () => { | |
this.update( ( state ) => { | |
const items = state.items; | |
const more_items = [ 1, 2, 3 ].map( () => Math.floor( Math.random() * 10000 ) ); | |
return { | |
...state, | |
loading: false, | |
items: items.concat( more_items ), | |
}; | |
} ); | |
}, 1000 ); | |
}, | |
} ); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const React = require( 'react' ); | |
class Store { | |
constructor( initial_state, actions ) { | |
this._state = initial_state; | |
this._actions = {}; | |
if ( actions ) { | |
for ( let name in actions ) { | |
this._actions[ name ] = actions[ name ].bind( this ); | |
} | |
} | |
this._callbacks = []; | |
} | |
get_state() { | |
return this._state; | |
} | |
update( reducer ) { | |
const new_state = ( typeof reducer === 'function' ) ? reducer( this._state ) : reducer; | |
if ( new_state !== undefined && new_state !== this._state ) { | |
this._state = new_state; | |
Promise.resolve().then( () => { | |
this._callbacks.forEach( ( callback ) => callback( new_state ) ); | |
} ); | |
} | |
} | |
subscribe( callback ) { | |
this._callbacks.push( callback ); | |
const unsubscribe = () => { | |
const index = this._callbacks.findIndex( callback ); | |
if ( index !== -1 ) { | |
this._callbacks.splice( index, 1 ); | |
} | |
}; | |
return unsubscribe; | |
} | |
} | |
function create_store( state, actions ) { | |
return new Store( state, actions ); | |
} | |
function connect( stores, component ) { | |
const connected = class extends React.Component { | |
constructor( props ) { | |
super( props ); | |
const state = {}; | |
for ( let key in stores ) { | |
const store = stores[ key ]; | |
state[ key ] = Object.assign( {}, store.get_state(), store._actions ); | |
} | |
this.state = state; | |
} | |
componentDidMount() { | |
this.unsubscribes = []; | |
for ( let key in stores ) { | |
const store = stores[ key ]; | |
this.unsubscribes.push( store.subscribe( ( new_state ) => { | |
this.setState( { | |
[ key ]: Object.assign( {}, new_state, store._actions ), | |
} ); | |
} ) ); | |
} | |
} | |
componentWillUnmount() { | |
this.unsubscribes.forEach( ( unsubscribe ) => unsubscribe() ); | |
} | |
render() { | |
return React.createElement( component, { ...this.props, ...this.state } ); | |
} | |
}; | |
connected.displayName = `Connect(${ component.name })`; | |
return connected; | |
} | |
module.exports = { | |
create_store: create_store, | |
connect: connect, | |
}; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const path_ = require( 'path' ); | |
const webpack = require( 'webpack' ); | |
module.exports = { | |
mode: 'development', | |
entry: { | |
index: './index.js', | |
}, | |
output: { | |
filename: '[name].js', | |
path: path_.resolve(__dirname, 'build'), | |
publicPath: '/build/', | |
}, | |
module: { | |
rules: [ | |
{ | |
test: /\.js$/, | |
exclude: [ | |
/node_modules/, | |
], | |
use: { | |
loader: 'babel-loader', | |
}, | |
}, | |
], | |
}, | |
resolve: { | |
extensions: [ '.js' ], | |
}, | |
plugins: [ | |
new webpack.NoEmitOnErrorsPlugin(), | |
], | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Что мне не нравится в redux/react-redux:
{ type, payload }
, который потом нужно еще засунуть в гигантский switch. И зачем на каждый чих заводить отдельный тип экшена. Вызов экшена должен сразу же вызывать свой редьюсер и менять стор.Тут некий proof of concept. Основные файлы:
App.js
,store-count.js
,store-items.js
,store.js
.Собрать пример так:
Главная идея: