// Ваш сайдбар обновится при каждом изменении selectedOption
connect((state, ownProps) => ({ selectedOption: state.selectedOption }))(SidebarComponent)
// Ваш селект обновляется в зависимости от изменения обоих свойств,
// и используя свойство onSelect может обновить selectedOption самостоятельно
connect(
(state, ownProps) => ({ options: state.options, selectedOption: state.selectedOption }),
(dispatch, ownProps) => ({ onSelect: (value) => dispatch(selectOption(value)) })
)(SelectComponent)
// в вашем './src/app.js'
import './styles/main.sass';
// в конфиге вебпака все надо упростить
// entry
{
app: './src/app.js',
}
// module.rules
{
test: /\.sass$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'sass-loader'
]
})
}
// plugins
new ExtractTextPlugin('styles/[name].css')
const API = process.env.api;
module.exports = {
...,
plugins: [
...,
new webpack.DefinePlugin({
API_URL: JSON.stringify(API)
})
]
}
{
"scripts": {
"build": "webpack --env.api=https://api.url/v1"
}
}
npm run build -- --env.api=https://api.url/v2
method(function() {
...
});
// заменить на
method(() => {
...
})
const that = this;
that.context;
const props = {
onClick: jest.fn()
};
let component;
beforeEach(() => {
component = enzyme.shallow(<Button {...props} />);
});
it('срабатывает событие onClick', () => {
field.simulate('click');
expect(props.onClick).toHaveBeenCalled();
});
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
fakeAuth.isAuthenticated ? (
<Component {...props}/>
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}}/>
)
)}/>
)
// getUsers - свойство из mapDispatchToProps, запрос на сервер, по завершению обновляет users в вашем Store
// users - свойство из mapStateToProps
class UserListContainer extends Component {
componentWillMount() {
this.props.getUsers();
}
render() {
const { users } = this.props;
if (!users) {
return <Spinner />
}
return <UserList data={users} />
}
}
class App extends Component {
render() {
const { appIsReady } = this.props;
// наш лоадер на весь экран при appIsReady: false
return (
<div>
{!appIsReady && <Spinner style='fullscreen' />}
</Switch>
<Route path='/' exact={true} component={Home} />
<Route component={NotFound} />
<Switch>
</div>
);
}
}
class Home extends Component {
componentWillMount() {
if (!this.props.token) {
this.props.getToken();
}
}
render() {
const { token } = this.props;
if (!token) {
return null;
}
// UserListContainer в willMount меняет appIsReady на true
return <Route path='/users' component={UserListContainer} />
}
}
export default function counter(state = initState, action) {
switch (action.type) {
case 'COUNTER:INCR':
console.log('INCREMENT');
state.num = state.num++;
return state;
case 'COUNTER:DECR':
console.log('DECREMENT');
state.num = state.num--;
return state;
case 'COUNTER:RES':
state.num = 0;
return state;
default:
return state;
}
}
state
в редьюсере получает store
вашего приложения, а в каждом блоке case вы должны возвращать либо не измененный store (default), либо копию вашего хранилища:export default function counter(state = initState, action) {
switch (action.type) {
case 'COUNTER:INCR':
return {
...state,
num: state.num + 1
};
case 'COUNTER:DECR':
return {
...state,
num: state.num - 1
};
case 'COUNTER:RES':
return {
...state,
num: 0
};
default:
return state;
}
}
{
...state,
num: state.num + 1
}
Object.assign({}, state, { num: state.num + 1 })
function MyComponent() {
this._bindedValue = 'someValue';
}
MyComponent.prototype.getBindedValue = function() {
return this._bindedValue;
}
MyComponent.prototype.setBindedValue = function(value) {
this._bindedValue = value;
this.makeSomethingWithNewValue();
}
var MyComponent() {
_bindedValue: 'someValue',
get bindedValue() {
return this._bindedValue;
}
set bindedValue(value) {
this._bindedValue = value;
this.makeSomethingWithNewValue();
}
}
output.publicPath: '/bundles/'
const gallery = new Gallery({})
- неплохо бы иметь доступ к управлению галереей через JS, например:gallery.pause();
gallery.next();
gallery.setOptions({})
var defaultOptions = {
...
}
privateMethod() {
...
}
function Gallery(options) {
this.options = extend(defaultOptions, options);
this.interval = this.createInterval();
...
}
Gallery.prototype.init = function() {
...
}
Gallery.prototype.next = function() {
...
}
Gallery.prototype.createInterval= function() {
...
}
function extend(a, b) {
...
}