Redux: рдпреВрдирд┐рдЯ рдкрд░реАрдХреНрд╖рдг рд╕реНрдорд╛рд░реНрдЯ рдШрдЯрдХреЛрдВ

рдХреЛ рдирд┐рд░реНрдорд┐рдд 20 рдЕрдЧре░ 2015  ┬╖  19рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: reduxjs/redux

рдореИрдВ рдкреНрд░рд▓реЗрдЦрди рдХреЗ рдпреВрдирд┐рдЯ рдкрд░реАрдХреНрд╖рдг рдЦрдВрдб рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкрдврд╝ рд░рд╣рд╛ рдерд╛ рдФрд░ рдЬрдмрдХрд┐ рдЗрд╕рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИ рдХрд┐ рдХреИрд╕реЗ рдПрдХ рдЧреВрдВрдЧрд╛ рдШрдЯрдХ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд┐рдпрд╛ рдЬрд╛рдП, рдЗрдХрд╛рдИ рдХреЛ "рд╕реНрдорд╛рд░реНрдЯ рдШрдЯрдХ" (рдЬреЛ рдХрдиреЗрдХреНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ) рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдирд╣реАрдВ рд╣реИред рдпрд╣ рдкрддрд╛ рдЪрд▓рд╛ рд╣реИ рдХрд┐ рдПрдХ рд╕реНрдорд╛рд░реНрдЯ рдШрдЯрдХ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдЗрдХрд╛рдИ рдЖрд╡рд░рдг рдШрдЯрдХ рдХреЗ рдХрд╛рд░рдг рдереЛрдбрд╝рд╛ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рд╣реИ рдЬреЛ рдХрдиреЗрдХреНрдЯ () рдмрдирд╛рддрд╛ рд╣реИред рд╕рдорд╕реНрдпрд╛ рдХрд╛ рдПрдХ рд╣рд┐рд╕реНрд╕рд╛ рдпрд╣ рд╣реИ рдХрд┐ рдХрдиреЗрдХреНрдЯ () рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдПрдХ рдШрдЯрдХ рдХреЛ рд▓рдкреЗрдЯрдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╣реИ рдХрд┐ рдПрдХ 'рд╕реНрдЯреЛрд░' рдкреНрд░реЛрдк (рдпрд╛ рд╕рдВрджрд░реНрдн) рд╣реЛред

рдореИрдВрдиреЗ рдРрд╕рд╛ рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдореЗрдВ рдПрдХ рджрд░рд╛рд░ рд▓реЗ рд▓реА рдФрд░ рдореБрдЭреЗ рдЙрдореНрдореАрдж рдереА рдХрд┐ рдЗрд╕реЗ рдкреВрд░рд╛ рдХрд░рдиреЗ рдХрд╛ рдПрдХ рдмреЗрд╣рддрд░ рддрд░реАрдХрд╛ рд╣реИ рдпрд╛ рдирд╣реАрдВред рдФрд░ рдЕрдЧрд░ рдореИрдВрдиреЗ рдЬреЛ рдХрд┐рдпрд╛ рд╣реИ, рд╡рд╣ рдЙрдЪрд┐рдд рд▓рдЧ рд░рд╣рд╛ рд╣реИ, рддреЛ рдореБрдЭреЗ рд▓рдЧрд╛ рдХрд┐ рдореИрдВ рдЗрд╕ рдкрд░реАрдХреНрд╖рдг рдЗрдХрд╛рдИ рдореЗрдВ рдХреБрдЫ рдЬрд╛рдирдХрд╛рд░реА рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдкреАрдЖрд░ рдХреЛ рдЖрдЧреЗ рдмрдврд╝рд╛рдКрдВрдЧрд╛ред

рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдореИрдВрдиреЗ рдбреЙрдХреНрд╕ рдХреЗ рдпреВрдирд┐рдЯ рдЯреЗрд╕реНрдЯ рд╕реЗрдХреНрд╢рди рдореЗрдВ рдореМрдЬреВрджрд╛ рдЙрджрд╛рд╣рд░рдг рдШрдЯрдХ рдХреЛ рд▓рд┐рдпрд╛, рдФрд░ рдЗрд╕реЗ рдХрдиреЗрдХреНрдЯ рд╕реЗ рдХрдиреЗрдХреНрдЯ рдХрд┐рдпрд╛ () рд░рд╛рдЬреНрдп рд╕реЗ рдбреЗрдЯрд╛ рдкрд╛рд╕ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдФрд░ рдкреНрд░реЗрд╖рдг-рдмрджреНрдз рдПрдХреНрд╢рди рдХреНрд░рд┐рдПрдЯрд░реНрд╕:

Header.js (рд╕реНрдорд╛рд░реНрдЯ рдШрдЯрдХ)
import React, { PropTypes, Component } from 'react';
import TodoTextInput from './TodoTextInput';
import TodoActions from '../actions/TodoActions';
import connect from 'redux-react';

class Header extends Component {
  handleSave(text) {
    if (text.length !== 0) {
      this.props.addTodo(text);
    }
  }

  render() {
    return (
      <header className='header'>
          <h1>{this.props.numberOfTodos + " Todos"}</h1>
          <TodoTextInput newTodo={true}
                         onSave={this.handleSave.bind(this)}
                         placeholder='What needs to be done?' />
      </header>
    );
  }
}

export default connect(
  (state) =>  {numberOfTodos: state.todos.length},
  TodoActions
)(Header);

рдпреВрдирд┐рдЯ рдЯреЗрд╕реНрдЯ рдлрд╛рдЗрд▓ рдореЗрдВ рдпрд╣ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд╕рдорд╛рди рджрд┐рдЦрддрд╛ рд╣реИред

Header.test.js
import expect from 'expect';
import jsdomReact from '../jsdomReact';
import React from 'react/addons';
import Header from '../../components/Header';
import TodoTextInput from '../../components/TodoTextInput';
const { TestUtils } = React.addons;

/**
 * Mock out the top level Redux store with all the required 
 * methods and have it return the provided state by default.
 * <strong i="13">@param</strong> {Object} state State to populate in store
 * <strong i="14">@return</strong> {Object} Mock store
 */
function createMockStore(state) {
  return {
    subscribe: () => {},
    dispatch: () => {},
    getState: () => {
      return {...state};
    }
  };
}

/**
 * Render the Header component with a mock store populated
 * with the provided state
 * <strong i="15">@param</strong> {Object} storeState State to populate in mock store
 * <strong i="16">@return</strong> {Object} Rendered output from component
 */
function setup(storeState) {
  let renderer = TestUtils.createRenderer();
  renderer.render(<Header store={createMockStore(storeState)} />);
  var output = renderer.getRenderedOutput();
  return output.refs.wrappedInstance();
}

describe('components', () => {
  jsdomReact();

  describe('Header', () => {
    it('should call call addTodo if length of text is greater than 0', () => {
      const output = setup({
        todos: [1, 2, 3]
      });
      var addTodoSpy = expect.spyOn(output.props, 'addTodo');

      let input = output.props.children[1];
      input.props.onSave('');
      expect(addTodoSpy.calls.length).toBe(0);
      input.props.onSave('Use Redux');
      expect(addTodoSpy.calls.length).toBe(1);
    });
  });
});

рдореИрдВрдиреЗ рд╕рдВрдмрдВрдзрд┐рдд рднрд╛рдЧреЛрдВ рдХреЛ рджрд┐рдЦрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕ рдкрд░реАрдХреНрд╖рдг рдХреЛ рдереЛрдбрд╝рд╛ рд╕рд░рд▓ рдХрд┐рдпрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдореИрдВ рдЬрд┐рд╕ рдЕрдирд┐рд╢реНрдЪрд┐рдд рдмрд┐рдВрджреБ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдирд┐рд╢реНрдЪрд┐рдд рд╣реВрдВ рд╡рд╣ createMockStore рдкрджреНрдзрддрд┐ рд╣реИред рдпрджрд┐ рдЖрдк рдмрд┐рдирд╛ рдХрд┐рд╕реА рдкреНрд░реЙрдкрд░ рдХреЗ рд╣реИрдбрд░ рдШрдЯрдХ рдХреЛ рдЯреНрд░рд╛рдИ рдФрд░ рд░реЗрдВрдбрд░ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ Redux (рдпрд╛ рд░рд┐рдПрдХреНрд╢рди-рд░рд┐рдбрдХреНрд╕) рджреНрд╡рд╛рд░рд╛ рдпрд╣ рдХрд╣рддреЗ рд╣реБрдП рдПрдХ рддреНрд░реБрдЯрд┐ рдбрд╛рд▓реА рдЬрд╛рддреА рд╣реИ рдХрд┐ рдШрдЯрдХ рдХреЗ рдкрд╛рд╕ store рдкреНрд░реЛрдк рдпрд╛ рд╕рдВрджрд░реНрдн рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдПрдХ рдмрдЪреНрдЪрд╛ рд╣реЛрдиреЗ рдХреА рдЙрдореНрдореАрдж рдХрд░рддрд╛ рд╣реИред <Provider> рдШрдЯрдХред рдЪреВрдБрдХрд┐ рдореИрдВ рдЕрдкрдиреА рдЗрдХрд╛рдИ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рд▓рд┐рдП рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛, рдЗрд╕рд▓рд┐рдП рдореИрдВрдиреЗ рдЗрд╕реЗ рдирдХрд▓реА рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рд╡рд┐рдзрд┐ рдмрдирд╛рдИ рдФрд░ рдкрд░реАрдХреНрд╖рдг рдХреЛ рдЙрд╕ рд╕реНрдерд┐рддрд┐ рдореЗрдВ рдкрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреА рдЬрд┐рд╕реЗ рд╡рд╣ рд╕реНрдЯреЛрд░ рдореЗрдВ рд╕реЗрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реИред

рдЗрд╕ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╕реЗ рдореИрдВ рдЬреЛ рд▓рд╛рдн рджреЗрдЦ рд╕рдХрддрд╛ рд╣реВрдВ, рд╡рд╣ рдпрд╣ рд╣реИ рдХрд┐ рдпрд╣ рдореБрдЭреЗ рдЕрдкрдиреЗ рдШрдЯрдХ рдХреЗ рднреАрддрд░ рдХрд╛рд░реНрдпреЛрдВ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЙрди рддрд░реАрдХреЛрдВ рдХреА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдЬреЛ рдореИрдВ рдХрдиреЗрдХреНрдЯ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ ()ред рдореИрдВ рдпрд╣рд╛рдБ рдЖрд╕рд╛рдиреА рд╕реЗ рдПрдХ рдФрд░ рджрд╛рд╡рд╛ рд▓рд┐рдЦ тАЛтАЛрд╕рдХрддрд╛ рд╣реВрдБ рдЬреЛ expect(output.props.numberOfTodos).toBe(3) рдЬреИрд╕рд╛ рдХреБрдЫ рдХрд░рддрд╛ рд╣реИ рдЬреЛ рдпрд╣ рд╕рддреНрдпрд╛рдкрд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдореЗрд░рд╛ mapStateToProps рдлрд╝рдВрдХреНрд╢рди рд╡рд╣реА рдХрд░ рд░рд╣рд╛ рд╣реИ рдЬрд┐рд╕рдХреА рдореБрдЭреЗ рдЕрдкреЗрдХреНрд╖рд╛ рд╣реИред

рдЗрд╕рдХрд╛ рдореБрдЦреНрдп рдирдХрд╛рд░рд╛рддреНрдордХ рдкрдХреНрд╖ рдпрд╣ рд╣реИ рдХрд┐ рдореБрдЭреЗ Redux рд╕реНрдЯреЛрд░ рдХрд╛ рдордЬрд╝рд╛рдХ рдЙрдбрд╝рд╛рдирд╛ рдкрдбрд╝ рд░рд╣рд╛ рд╣реИ, рдЬреЛ рдХрд┐ рдЬрдЯрд┐рд▓ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдЖрдВрддрд░рд┐рдХ Redux рддрд░реНрдХ рдХрд╛ рд╣рд┐рд╕реНрд╕рд╛ рд╣реИ рдФрд░ рдЗрд╕рдореЗрдВ рдмрджрд▓рд╛рд╡ рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рдЬрд╛рд╣рд┐рд░ рд╣реИ рдореЗрд░реА рдЗрдХрд╛рдИ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рд▓рд┐рдП рдореИрдВрдиреЗ рдЗрди рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЛ рдПрдХ рд╕рд╛рдорд╛рдиреНрдп рдЗрдХрд╛рдИ рдкрд░реАрдХреНрд╖рдг рдЙрдкрдпреЛрдЧрд┐рддрд╛ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░ рджрд┐рдпрд╛ рд╣реИ рддрд╛рдХрд┐ рдпрджрд┐ рд╕реНрдЯреЛрд░ рдХреЗ рддрд░реАрдХреЛрдВ рдореЗрдВ рдмрджрд▓рд╛рд╡ рдЖрдП, рддреЛ рдореБрдЭреЗ рдХреЗрд╡рд▓ рдПрдХ рдЬрдЧрд╣ рдЕрдкрдирд╛ рдХреЛрдб рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред

рд╡рд┐рдЪрд╛рд░? рдХреНрдпрд╛ рдХрд┐рд╕реА рдФрд░ рдиреЗ рд╕реНрдорд╛рд░реНрдЯ рдЙрдкрдХрд░рдгреЛрдВ рдХреЗ рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП рдпреВрдирд┐рдЯ рдХреЗ рд╕рд╛рде рдкреНрд░рдпреЛрдЧ рдХрд┐рдпрд╛ рд╣реИ рдФрд░ рдЪреАрдЬреЛрдВ рдХреЛ рдХрд░рдиреЗ рдХрд╛ рдПрдХ рдмреЗрд╣рддрд░ рддрд░реАрдХрд╛ рдкрд╛рдпрд╛ рд╣реИ?

discussion question

рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА

@ernieturner рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ @ghengeveld рдХрд╛ рдорддрд▓рдм рдпрд╣ рд╣реИ рдХрд┐ рдпрджрд┐ рдЖрдк рд╣рд░ рдЬрдЧрд╣ ES6 рдореЙрдбреНрдпреВрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рд╕рдЬрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИрдбрд░ рдШрдЯрдХ рдЕрднреА рднреА рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдХреЗ рд░реВрдк рдореЗрдВ рдирд┐рд░реНрдпрд╛рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рдФрд░ рдПрдХ рд╕рд╛рджрд╛ рд░рд┐рдПрдХреНрдЯ рдШрдЯрдХ рдПрдХ рдЕрддрд┐рд░рд┐рдХреНрдд рдирд╛рдорд┐рдд рдирд┐рд░реНрдпрд╛рдд рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП -

//header.js
export class HeaderDumbComponent {
  render() {
    return <header><div>...</div></header>;
  }
}

export default connect(
  (state) =>  {numberOfTodos: state.todos.length},
  TodoActions
)(HeaderDumbComponent);

рдЗрд╕ рджреЛрд╣рд░реЗ рдирд┐рд░реНрдпрд╛рдд рдХреЗ рд╕рд╛рде, рдЖрдкрдХрд╛ рдореБрдЦреНрдп рдРрдк import Header from './header.js' рд╕рд╛рде рдкрд╣рд▓реЗ рдХреА рддрд░рд╣ рд╕реНрдорд╛рд░реНрдЯ рдШрдЯрдХ рдХрд╛ рдЙрдкрднреЛрдЧ рдХрд░ рд╕рдХрддрд╛ рд╣реИ, рдЬрдмрдХрд┐ рдЖрдкрдХреЗ рдкрд░реАрдХреНрд╖рдг Redux рдХреЛ рдмрд╛рдпрдкрд╛рд╕ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдХреЛрд░ рдШрдЯрдХ рдХреЗ рд╡реНрдпрд╡рд╣рд╛рд░ рдХрд╛ рд╕реАрдзреЗ import {HeaderDumbComponent} from '../components/header.js' рд╕рд╛рде рдкрд░реАрдХреНрд╖рдг рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

рд╕рднреА 19 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

рдЗрд╕рдХрд╛ рд╡рд┐рдХрд▓реНрдк рд╣реИрдбрд░ (рдЕрдШреЛрд╖рд┐рдд) рд╣реИрдбрд░ рд╡рд░реНрдЧ рдХреЛ рднреА рдирд┐рд░реНрдпрд╛рдд рдХрд░рдирд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЗрд╕реЗ рдЕрд▓рдЧ рд╕реЗ рдЖрдпрд╛рдд рдФрд░ рдкрд░реАрдХреНрд╖рдг рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдЖрдкрдХреЛ рд╣рд░ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ jsdomReact рдХреЙрд▓ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ, рдХрдо рд╕реЗ рдХрдо рдпрджрд┐ рдЖрдк рдореЛрдЪрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣реЗ рд╣реИрдВред рдпрд╣рд╛рдБ рдореЗрд░рд╛ setup.js рд╣реИ:

import jsdom from 'jsdom';
import ExecutionEnvironment from 'react/lib/ExecutionEnvironment';

if (!global.document || !global.window) {
  global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
  global.window = document.defaultView;
  global.navigator = window.navigator;

  ExecutionEnvironment.canUseDOM = true;

  window.addEventListener('load', () => {
    console.log('JSDom setup completed: document, window and navigator are now on global scope.');
  });
}

рдпрд╣ --require рдХрдорд╛рдВрдб рд▓рд╛рдЗрди рд╡рд┐рдХрд▓реНрдк рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд▓реЛрдб рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ: mocha -r babelhook -r test/setup --recursive (babelhook require('babel-core/register') рд▓рд┐рдП рдПрдХ рдХреЙрд▓ рд╣реИ)ред

рд╣рд╛рдВ, рдореИрдВ рд╣реЗрдбрд░ рд╡рд░реНрдЧ рдФрд░ рд╕рдЬрд╛рдП рдЧрдП рджреЛрдиреЛрдВ рдХреЛ рдирд┐рд░реНрдпрд╛рдд рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ, рд▓реЗрдХрд┐рди рдореИрдВ рдЕрдкрдиреА рдЗрдХрд╛рдИ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рд▓рд┐рдП рд╕реНрд░реЛрдд рдХреЛрдб рдмрджрд▓рдиреЗ рд╕реЗ рдмрдЪрдиреЗ рдХреА рдЙрдореНрдореАрдж рдХрд░ рд░рд╣рд╛ рдерд╛ред рдРрд╕рд╛ рдХрд░рдиреЗ рд╕реЗ рдореБрдЭреЗ рдЙрди рд╕рднреА рд╕реНрдерд╛рдиреЛрдВ рдХреЛ рдмрджрд▓рдирд╛ рдкрдбрд╝реЗрдЧрд╛ рдЬрд┐рдирдореЗрдВ рдПрдХ рд╕рдЬрд╛рдпрд╛ рд╣реБрдЖ рдШрдЯрдХ рд╢рд╛рдорд┐рд▓ рд╣реИ (рдЬреИрд╕реЗ import {DecoratedHeader} from './components/Header' рдмрдЬрд╛рдп рд╕рд┐рд░реНрдл import Header from / рдШрдЯрдХреЛрдВ / Header`)ред

Jsdom рд╕реЗрдЯрдЕрдк рдХреЗ рд▓рд┐рдП, рдореИрдВ рд╡рд┐рд╢реБрджреНрдз рд░реВрдк рд╕реЗ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХреЛ рдЙрдкрдпреЛрдЧ рдХреЗ рдорд╛рдорд▓реЗ рдХреЛ рджрд┐рдЦрд╛рдиреЗ рдХреЗ рддрд░реАрдХреЗ рдХреЗ рд░реВрдк рдореЗрдВ рдбреЙрдХреНрд╕ рд╕реЗ рдХреЙрдкреА рдХрд░ рд░рд╣рд╛ рдерд╛, рдореИрдВ рдЗрд╕реЗ рдЕрдкрдиреЗ рдореБрдЦреНрдп рд╕реЗрдЯрдЕрдк рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░ рд░рд╣рд╛ рд╣реВрдБ, рд╕рд┐рд░реНрдл рдПрдХ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд░реВрдк рдореЗрдВред

рдореИрдВрдиреЗ рдЕрднреА рджреЗрдЦрд╛ рдХрд┐ рдореЗрд░реЗ рдХреЛрдб рдХрд╛ рдирдореВрдирд╛ рдЕрдзреВрд░рд╛ рдерд╛ред рд╕реАрдзреЗ рддреМрд░ рдкрд░ рдПрдХ рд╕реНрдорд╛рд░реНрдЯ рдШрдЯрдХ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЗ рдкрд╛рд╕ рдХреБрдЫ рдЙрдкрдпреЛрдЧреА рд╡рд┐рдзрд┐ рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдП, рдЬрд┐рд╕рд╕реЗ рдЖрдкрдХреЛ рдХреЗрд╡рд▓ refs.wrappedInstance рдорд┐рд▓реЗрдВрдЧреЗ, рдХреЗрд╡рд▓ рдЗрд╕рдХреЗ рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк рд░реЗрдВрдбрд░ рдХрд░рдиреЗ рд╕реЗ рдЖрдкрдХреЛ рдХрдиреЗрдХреНрдЯ-рд╕рдЬрд╛ рд╣реБрдЖ рдШрдЯрдХ (рдКрдкрд░ рд╕реЗрдЯрдЕрдк рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рдЕрджреНрдпрддрди) рдорд┐рд▓реЗрдЧрд╛ ред рдпрд╣ рдлрд┐рд░ рд╕реЗ рдереЛрдбрд╝реЗ рдпреВрдирд┐рдЯ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЛ Redux рдХреЗ рдЖрдВрддрд░рд┐рдХ (рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛- redux рдХреЗ рдЖрдВрддрд░рд┐рдХ) рдкрд░ рдирд┐рд░реНрднрд░ рдХрд░рддрд╛ рд╣реИред рддреЛ рдпрд╣ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдереЛрдбрд╝рд╛ рдирд╛рдЬреБрдХ рд▓рдЧрддрд╛ рд╣реИред

@ernieturner рд╣рдо рдЗрд╕рдХреЗ рд▓рд┐рдП getWrappedInstance() public API рднреА рдкреНрд░рджрд╛рди рдХрд░рддреЗ рд╣реИрдВ рддрд╛рдХрд┐ рдЖрдк рдЗрд╕ рдкрд░ рднрд░реЛрд╕рд╛ рдХрд░ рд╕рдХреЗрдВ рдпрджрд┐ рдЖрдк refs рд╕реАрдзреЗ рдПрдХреНрд╕реЗрд╕ рдХрд░рдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЪрд┐рдВрддрд┐рдд рд╣реИрдВред рдФрд░ рдЗрд╕ рдкрд╛рд░рджрд░реНрд╢реА рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛-рдкрд░реАрдХреНрд╖рдг-рд╡реГрдХреНрд╖ рдЬреИрд╕реА рдЙрдкрдпреЛрдЧрд┐рддрд╛рдПрдБ рднреА рд╣реИрдВред

@ernieturner рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ @ghengeveld рдХрд╛ рдорддрд▓рдм рдпрд╣ рд╣реИ рдХрд┐ рдпрджрд┐ рдЖрдк рд╣рд░ рдЬрдЧрд╣ ES6 рдореЙрдбреНрдпреВрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рд╕рдЬрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИрдбрд░ рдШрдЯрдХ рдЕрднреА рднреА рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдХреЗ рд░реВрдк рдореЗрдВ рдирд┐рд░реНрдпрд╛рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рдФрд░ рдПрдХ рд╕рд╛рджрд╛ рд░рд┐рдПрдХреНрдЯ рдШрдЯрдХ рдПрдХ рдЕрддрд┐рд░рд┐рдХреНрдд рдирд╛рдорд┐рдд рдирд┐рд░реНрдпрд╛рдд рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП -

//header.js
export class HeaderDumbComponent {
  render() {
    return <header><div>...</div></header>;
  }
}

export default connect(
  (state) =>  {numberOfTodos: state.todos.length},
  TodoActions
)(HeaderDumbComponent);

рдЗрд╕ рджреЛрд╣рд░реЗ рдирд┐рд░реНрдпрд╛рдд рдХреЗ рд╕рд╛рде, рдЖрдкрдХрд╛ рдореБрдЦреНрдп рдРрдк import Header from './header.js' рд╕рд╛рде рдкрд╣рд▓реЗ рдХреА рддрд░рд╣ рд╕реНрдорд╛рд░реНрдЯ рдШрдЯрдХ рдХрд╛ рдЙрдкрднреЛрдЧ рдХрд░ рд╕рдХрддрд╛ рд╣реИ, рдЬрдмрдХрд┐ рдЖрдкрдХреЗ рдкрд░реАрдХреНрд╖рдг Redux рдХреЛ рдмрд╛рдпрдкрд╛рд╕ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдХреЛрд░ рдШрдЯрдХ рдХреЗ рд╡реНрдпрд╡рд╣рд╛рд░ рдХрд╛ рд╕реАрдзреЗ import {HeaderDumbComponent} from '../components/header.js' рд╕рд╛рде рдкрд░реАрдХреНрд╖рдг рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

@ eugene1g @ghengeveld рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рд╕рдорд╕реНрдпрд╛ рдХрд╛ рдПрдХ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд╕рдорд╛рдзрд╛рди рд╣реИред "рд▓реЗрдЦрди рдкрд░реАрдХреНрд╖рдг" рдХреЗ рд▓рд┐рдП рдПрдХ рдкреАрдЖрд░ рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдмреЗрдЭрд┐рдЭрдХ рдбреЙрдХреНрдЯрд░ рдЗрд╕реЗ рд╕рдордЭрд╛рддреЗ рд╣реБрдП!

@ eugene1g рдореЗрд░рд╛ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдпрд╣реА рдорддрд▓рдм рд╣реИред
@gaearon рдореИрдВ рдРрд╕рд╛ рдХрд░реВрдБрдЧрд╛ред

@ghengeveld рдЧрд▓рддрдлрд╣рдореА рдХреЗ рд▓рд┐рдП рдорд╛рдлреАред рдореИрдВ рдЕрднреА рднреА рдХреЙрдордирдЬрд╕ рд╢реИрд▓реА рдХреЗ рдЖрдпрд╛рдд рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ, рдЗрд╕рд▓рд┐рдП рдореИрдВ рдЕрднреА рднреА рдИрдПрд╕ 6 рдореЙрдбреНрдпреВрд▓ рдкрд░ рдмрд╣реБрдд рдЕрдзрд┐рдХ рдХрдареЛрд░ рд╣реВрдВред рдпрд╣ рдбреЙрдХреНрд╕ рдореЗрдВ рдЙрдЬрд╛рдЧрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрдЪреНрдЫрд╛ рд╕рдорд╛рдзрд╛рди рдХреА рддрд░рд╣ рдкреНрд░рддреАрдд рд╣реЛрддрд╛ рд╣реИред рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЖрдк рдореИрдкрд╕реНрдЯреИрдЯрдЯреЙрдкреНрд░реЛрдкреНрд╕ / рдореИрдкрдбрд╛рдЗрд╕реНрдкреЗрдХреНрдЯрдЯрд╛рдЙрдкреНрд░реЙрдкреНрд╕ рдХреЗ рддрд░реАрдХреЛрдВ рдХреЛ рднреА рдЙрдЬрд╛рдЧрд░ рдХрд░ рд╕рдХрддреЗ рдереЗ, рд╕рд╛рде рд╣реА рдЙрди рд▓реЛрдЧреЛрдВ рдХреЛ рд╕реНрдЯреЛрд░ рд╕реЗ рдмрд╛рд╣рд░ рдореЙрдХрд┐рдВрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реАрдХреНрд╖рдг рдХрд┐рдП рдмрд┐рдирд╛ рдЕрдиреБрдорддрд┐ рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП, рдЬреИрд╕реЗред

export class HeaderDumpComponent ...

export function mapStateToProps(state) ... 
export function mapDispatchToProps(dispatch) ...

export default connect(mapStateToProps, mapDispatchToProps)(HeaderDumpComponent);

рдпрд╣ рдмрд╣реБрдд рд╕рдВрднрд╡ рд╣реИ рдХрд┐ рдирдХреНрд╢реЗ рдХреЗ рдЕрдзрд┐рдХрд╛рдВрд╢ рддрд░реАрдХреЗ рдпреВрдирд┐рдЯ рдкрд░реАрдХреНрд╖рдг рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рд╕рд░рд▓ рд╣реЛрдВрдЧреЗ, рд▓реЗрдХрд┐рди рдореЗрд░реЗ рдкрд╛рд╕ рдЙрдирдореЗрдВ рд╕реЗ рдХреБрдЫ рд╕рддреНрдпрд╛рдкрд┐рдд рдХрд░рдиреЗ рд▓рд╛рдпрдХ рд╣реЛрдВрдЧреЗ рдХреНрдпреЛрдВрдХрд┐ рд╡реЗ рдореЗрд░реЗ рдШрдЯрдХреЛрдВ рдХреА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХрд╛ рдПрдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣рд┐рд╕реНрд╕рд╛ рд╣реИрдВред

рд╕рднреА рдХреЗ рд╕реБрдЭрд╛рд╡реЛрдВ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж, рдЕрдЧрд░ рдЖрдкрдХреЛ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝реАрдХрд░рдг рдореЗрдВ рдХреЛрдИ рдорджрдж рдЪрд╛рд╣рд┐рдП, рддреЛ рдореБрдЭреЗ рдмрддрд╛рдПрдВред

рд╣рдо рдЕрдиреБрд╢рдВрд╕рд┐рдд рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░ рд░рд╣реЗ рд╣реИрдВ рдЬреИрд╕рд╛ рдХрд┐ рдЕрднреА рдКрдкрд░ рдЙрд▓реНрд▓рд┐рдЦрд┐рдд рд╣реИ, рд▓реЗрдХрд┐рди рд╣рдо рдЗрд╕рдХреЗ рд╕рд╛рде рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╕рд╣рдЬ рдорд╣рд╕реВрд╕ рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВред рдпрд╣ рдПрдХреАрдХрд░рдг рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рдмрд╛рд╣рд░ connect рдЕрдкреНрд░рдорд╛рдгрд┐рдд рдХрд┐рдП рдЧрдП рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рдЫреЛрдбрд╝ рджреЗрддрд╛ рд╣реИред рдореИрдВ рд╕рдордЭрддрд╛ рд╣реВрдВ рдХрд┐ рдПрдХ рдирдХрд▓реА рд╕реНрдЯреЛрд░ рд╕рдВрднрд╛рд╡рд┐рдд рд░реВрдк рд╕реЗ рднрдВрдЧреБрд░ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдореБрдЭреЗ рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЬрд╣рд╛рдВ Redux-mock-store рдЬреИрд╕реА рдХреЛрдИ рдЪреАрдЬ рдХреВрдж рд╕рдХрддреА рд╣реИ, рдЕрдЧрд░ рд╡рд╣ рдХрдВрдкреЛрдиреЗрдВрдЯ рдкрд░реАрдХреНрд╖рдг рдХрд╛ рд╕рдорд░реНрдерди рдХрд░ рд╕рдХрддреА рд╣реИред рдЖрдк рдЙрд╕ рджрд┐рд╢рд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреИрд╕рд╛ рдорд╣рд╕реВрд╕ рдХрд░рддреЗ рд╣реИрдВ?

@carpeliam

рдХреНрдпреЛрдВ рди рдХреЗрд╡рд▓ рдПрдХ рдирд┐рдпрдорд┐рдд рд╕реНрдЯреЛрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ рдФрд░ рдЗрд╕реЗ рдПрдХ рд╡рд┐рд╢реЗрд╖ рдкреНрд░рд╛рд░рдВрднрд┐рдХ рдЕрд╡рд╕реНрдерд╛ рдХреЗ рд╕рд╛рде рд╣рд╛рдЗрдбреНрд░реЗрдЯ рдХрд░реЗрдВ?
рдЖрдкрдХреЛ рдХреЗрд╡рд▓ рд╕реНрдЯреЛрд░ рдХреЛ рдореЙрдХ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдпрджрд┐ рдЖрдк рдкрд░реАрдХреНрд╖рдг рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдХрд┐ рдХреМрди рд╕реЗ рдХрд╛рд░реНрдп рднреЗрдЬреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВред

@carpeliam

рдХреГрдкрдпрд╛ рдЗрд╕ рд░реЗрдкреЛ рдореЗрдВ counter рдФрд░ todomvc рд▓рд┐рдП рдпреВрдирд┐рдЯ рдкрд░реАрдХреНрд╖рдг рджреЗрдЦреЗрдВред

рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдПрдХ рд╕рдорд╕реНрдпрд╛ рдереА рдЬрд╣рд╛рдВ рд╣рдо рдПрдХ рдЕрдиреНрдп рдШрдЯрдХ рдХреЗ рдЕрдВрджрд░ рдПрдХ рд╕реНрдорд╛рд░реНрдЯ рдШрдЯрдХ рдкреНрд░рджрд╛рди рдХрд░ рд░рд╣реЗ рдереЗ рдФрд░ рдЙрдерд▓реЗ рд░реЗрдВрдбрд░рд░ (рдШрдЯрдХ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдП рдЧрдП рдШрдЯрдХ) рдпрд╛ рддрд░реАрдХреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рдереЗред рд╣рдордиреЗ рдЬреЛ рдХрд┐рдпрд╛ рд╡рд╣ рд╕реНрдЯрдм (рд╕рд┐рдиреЙрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ) рдПрдХ рд╕рд╛рдзрд╛рд░рдг рд░рд┐рдПрдХреНрдЯ рдШрдЯрдХ рдХреЛ рд╡рд╛рдкрд╕ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рд╡рд╛рдкрд╕ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрдиреЗрдХреНрдЯ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХрд┐рдпрд╛ред

рдпрд╣ рднрдВрдЧреБрд░ рд╣реИ рдФрд░ рд╣рдо рдЙрдореНрдореАрдж рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рдЙрдерд▓реЗ рд░реЗрдВрдбрд░рд░ рдореЗрдВ рдЪрд▓реЗ рдЬрд╛рдиреЗ рдХреЗ рдмрд╛рдж рдПрдХ рдмрд╛рд░ рд╣рдо рд░рд┐рдПрдХреНрдЯ 0.14 рдкрд░ рдорд╛рдЗрдЧреНрд░реЗрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдЗрд╕ рдкрджреНрдзрддрд┐ рдиреЗ рд╣рдореЗрдВ рд╕рдордп рдкрд░ рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП рдЕрдирдмреНрд▓реЙрдХ рдХрд░ рджрд┐рдпрд╛ред

рдореБрдЭреЗ рдЗрд╕рд╕реЗ рдереЛрдбрд╝реА рдкрд░реЗрд╢рд╛рдиреА рд╣реЛ рд░рд╣реА рд╣реИред рдореИрдВ рд░рд┐рдПрдХреНрдЯ рдФрд░ рд░реЗрдбрдХреНрд╕ рджреЛрдиреЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдЬреИрд╕рд╛ рдирд╣реАрдВ рд╣реВрдВ, рдЗрд╕рд▓рд┐рдП рдпрд╣ рд╕рдВрднрд╡ рд╣реИ рдХрд┐ рдореБрдЭреЗ рд╡рд╛рдкрд╕ рдкрдХрдбрд╝рд╛ рдЬрд╛рдПред рдЖрдк рдЗрд╕реЗ рдХреИрд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд░ рдкрд╛рдП? @songawee

рдкреНрд░рд╕реНрддрд╛рд╡рд┐рдд рдбрдмрд▓ рдПрдХреНрд╕рдкреЛрд░реНрдЯ рд╡рд┐рдзрд┐ рдЪреБрдирд┐рдВрджрд╛ (mapStoreToState) рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдЫреЛрдбрд╝ рджреЗрддрд╛ рд╣реИред рд╕реНрд╡рддрдВрддреНрд░ рд░реВрдк рд╕реЗ рдЗрд╕рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕реЗ рдирд┐рд░реНрдпрд╛рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдлрд┐рд░ рднреА рдкрд░реАрдХреНрд╖рдг рдХреЗ рдирд╛рдо рдкрд░ рдПрдХ рдФрд░ рдмрджрд▓рд╛рд╡ред

рдореИрдВ рдХрдиреЗрдХреНрдЯ рдХреЗ рд▓рд┐рдкрдЯреЗ рдШрдЯрдХ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдерд▓рд╛-рдкрддрд▓рд╛ рд╣реЛрдиреЗ рдХрд╛ рд░рд╛рд╕реНрддрд╛ рдЦреЛрдЬрдиреЗ рдореЗрдВ рджрд┐рд▓рдЪрд╕реНрдкреА рд░рдЦрддрд╛ рд╣реВрдВред рдореЗрд░реА рд╡рд░реНрддрдорд╛рди рд╕рдорд╕реНрдпрд╛ рдпрд╣ рд╣реИ рдХрд┐ рдЙрдерд▓реЗ рд░реЗрдВрдбрд░рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╕рдордп рдпрд╣ рдХреЗрд╡рд▓ рдХрдиреЗрдХреНрдЯ рдШрдЯрдХ рдХреЛ рд╡рд╛рдкрд╕ рдкрд╛рд░рд┐рдд рдХрд░рддрд╛ рд╣реИ, рдЬрд┐рд╕рдХреА рдЙрдореНрдореАрдж рдХреА рдЬрд╛рдиреА рд╣реИред

рдкреНрд░рд╕реНрддрд╛рд╡рд┐рдд рдбрдмрд▓ рдПрдХреНрд╕рдкреЛрд░реНрдЯ рд╡рд┐рдзрд┐ рдЪреБрдирд┐рдВрджрд╛ (mapStoreToState) рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдЫреЛрдбрд╝ рджреЗрддрд╛ рд╣реИред рд╕реНрд╡рддрдВрддреНрд░ рд░реВрдк рд╕реЗ рдЗрд╕рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕реЗ рдирд┐рд░реНрдпрд╛рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдлрд┐рд░ рднреА рдкрд░реАрдХреНрд╖рдг рдХреЗ рдирд╛рдо рдкрд░ рдПрдХ рдФрд░ рдмрджрд▓рд╛рд╡ред

рд╕рдЦреНрддреА рд╕реЗ рдирд╣реАрдВ "рдкрд░реАрдХреНрд╖рдг рдХреЗ рдирд╛рдо рдкрд░"ред рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рд╣рдо рдЖрдкрдХреЛ рдЪрдпрдирдХрд░реНрддрд╛рдУрдВ рдХреЗ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░реЛрддреНрд╕рд╛рд╣рд┐рдд рдХрд░рддреЗ рд╣реИрдВ (рдЬреЛ рдХрд┐ mapStateToProps рдЕрдВрддрддрдГ рд╣реИ) рдЕрдкрдиреЗ рд░реАрдбреНрдпреВрд╕рд░ рдХреЗ рд╕рд╛рде, рдФрд░ рдЙрдиреНрд╣реЗрдВ рдПрдХ рд╕рд╛рде рдкрд░реАрдХреНрд╖рдг рдХрд░рддреЗ рд╣реИрдВред рдпрд╣ рддрд░реНрдХ UI рд╕реЗ рд╕реНрд╡рддрдВрддреНрд░ рд╣реИ рдФрд░ рдЗрд╕рдХреЗ рд╕рд╛рде рдмрдВрдбрд▓ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рд╣реИред рдХреБрдЫ рдЪрдпрдирдХрд░реНрддрд╛рдУрдВ рдХреЗ рд▓рд┐рдП shopping-cart рдЙрджрд╛рд╣рд░рдг рджреЗрдЦреЗрдВред

рддреЛ @gaearon , рдХреНрдпрд╛ рдЖрдк рдЗрд╕ _selector_ рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд░рд╛рдЬреНрдп рд╕реЗ рд╕рднреА рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХрд╛ рд╕реБрдЭрд╛рд╡ рджреЗ рд░рд╣реЗ рд╣реИрдВ? рдХреНрдпрд╛ рдпрд╣ рдмрд╣реБрдд рд╕рд╛рд░реЗ рдЕрдирд╛рд╡рд╢реНрдпрдХ рдЙрдкрд░рд┐ рдХрд╛ рдкрд░рд┐рдЪрдп рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЬреНрдпрд╛рджрд╛рддрд░ рдорд╛рдорд▓реЛрдВ рдореЗрдВ рд▓реЛрдЧ рд╕рд┐рд░реНрдл рд░рд╛рдЬреНрдп рд╕реЗ рд╕рдВрдкрддреНрддрд┐рдпреЛрдВ рдХрд╛ рдПрдХ рдЧреБрдЪреНрдЫрд╛ рдкрдврд╝реЗрдВрдЧреЗ рдФрд░ рдЙрдиреНрд╣реЗрдВ рдШрдЯрдХреЛрдВ рдкрд░ рд╕рд╣рд╛рд░рд╛ рджреЗрдВрдЧреЗ?

рд╣рд╛рдВ, Redux рдХреЗ рд▓рд┐рдП рд╕рд╛рдорд╛рдиреНрдп рд░реВрдк рд╕реЗ рд╕реБрдЭрд╛рдпрд╛ рдЧрдпрд╛ рдкреИрдЯрд░реНрди state.some.nested.field рд╕реАрдзреЗ рдПрдХреНрд╕реЗрд╕ рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп рдЪрдпрдирдХрд░реНрддрд╛ рдлрд╝рдВрдХреНрд╢рдВрд╕ рдХрд╛ рдмрд╣реБрдд рдЕрдзрд┐рдХ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╣реИред рд╡реЗ рдмрд╣реБрдд рд╕рд░рд▓ "рд╕рд╛рджреЗ" рдХрд╛рд░реНрдп рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдЖрдорддреМрд░ рдкрд░ рд░реЗрд╕реЗрд▓реЗрдЯ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рд╕рд╛рде рд░рдЦрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬреЛ рд╕рдВрд╕реНрдорд░рдг рдХреНрд╖рдорддрд╛рдУрдВ рдХреЛ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред

рд╡рд╣ рдХреЛрдИ рдЕрддрд┐рд░рд┐рдХреНрдд рдЙрдкрд░рд┐ рдХреНрдпреЛрдВ рдмрдирд╛рдПрдЧрд╛?

рдореИрдВ рдпрд╣рд╛рдБ рдмрддрд╛рдП рдЕрдиреБрд╕рд╛рд░ рджреЛрд╣рд░рд╛ рдирд┐рд░реНрдпрд╛рдд рдХрд░ рд░рд╣рд╛ рд╣реВрдБ рд▓реЗрдХрд┐рди connect рд╕реНрд░реЛрдд рдХреЛ рдкрдврд╝рдХрд░ рдореБрдЭреЗ рд▓рдЧрд╛ рдХрд┐ "рдХрдВрдЯреЗрдирд░" WrappedComponent рд╕реНрдерд┐рд░ рд╕рдВрдкрддреНрддрд┐ рдореЗрдВ "рдЧреВрдВрдЧрд╛" рдШрдЯрдХ рдХрд╛ рд╕рдВрджрд░реНрдн рд░рдЦрддрд╛ рд╣реИ:

рдЗрд╕рд▓рд┐рдП рдЗрд╕рдХреЗ рдмрдЬрд╛рдп:

// header.js
export const HeaderDumbComponent = (props) => <header><div>...</div></header>
export default connect(mapStateToProps)(HeaderDumbComponent)


// header.spec.js
import { HeaderDumbComponent } from './header'

it('renders', () => {
  expect(<HeaderDumbComponent />).to.not.be.null
})

рдЖрдк WrappedComponent рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рджреЛрд╣рд░реЗ рдирд┐рд░реНрдпрд╛рдд рд╕реЗ рдмрдЪ рд╕рдХрддреЗ рд╣реИрдВ рдЬрд╣рд╛рдВ рдЖрдкрдХреЛ "рдЧреВрдВрдЧрд╛" рд╕рдВрд╕реНрдХрд░рдг рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ:

// header.js
const HeaderDumbComponent = (props) => <header><div>...</div></header>
export default connect(mapStateToProps)(HeaderDumbComponent)


// header.spec.js
import Header from './header'

it('renders', () => {
  expect(<Header.WrappedComponent />).to.not.be.null
})

@gaearon - WrappedComponent рдбреЙрдХреНрд╕ рдореЗрдВ рджреА рдЧрдИ рдПрдХ рдмрд╣реБрдд рд╣реА рд╕реНрдерд┐рд░ рд╕рдВрдкрддреНрддрд┐ рд╣реИред рдХреНрдпрд╛ рдЖрдк рдХрд┐рд╕реА рднреА рдХрд╛рд░рдг рд╕реЗ рдЗрд╕ рддрд░рд╣ рд╕реЗ рдЗрд╕рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рдиреЗ рдХреЗ рдЦрд┐рд▓рд╛рдл рд╕рд▓рд╛рд╣ рджреЗрдВрдЧреЗ?

рдореИрдВ рдЬреЛ рдХрд░рддрд╛ рд╣реИ, рд╡рд╣ рдЕрд▓рд┐рдЦрд┐рдд рдШрдЯрдХ рдХреЛ рдирд┐рд░реНрдпрд╛рдд рдХрд░рддрд╛ рд╣реИ, рдФрд░ mapStateToProps рдФрд░ mapDispatchToProps рдХреЛ рднреА рдирд┐рд░реНрдпрд╛рдд рдХрд░рддрд╛ рд╣реИ рддрд╛рдХрд┐ рдореИрдВ рдЙрди рдХрд╛рд░реНрдпреЛрдВ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░ рд╕рдХреВрдВред рдпрд╣рд╛рдБ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИ:

import {ManageCoursePage, mapStateToProps, mapDispatchToProps} from './ManageCoursePage';
describe("mapStateToProps", () => {
    function setup(courses, authors, id) {
        const state = {
            authors: authors
        };

        const ownProps = {
            params: {
                id: id
            }
        };

        return mapStateToProps(state, ownProps);
    }

    it('sets the course when a course id is set', () => {
        const courses = [ { id: "1", foo: "bar" }, { id: "2", foo: "notbar" } ];
        const authors = [];
        const id = "1";

        const props = setup(courses, authors, id);

        expect(props.course).toBe(courses[0]);
    });

    it('sets the course when a course id is not set', () => {
        const courses = [ { id: "1", foo: "bar" }, { id: "2", foo: "notbar" } ];
        const authors = [];

        const props = setup(courses, authors, null);

        expect(props.course).toEqual({});
    });

    it('sets the course when a course id is set that is not present in the courses list', () => {
        const courses = [ { id: "1", foo: "bar" }, { id: "2", foo: "notbar" } ];
        const authors = [];
        const id = "42";

        const props = setup(courses, authors, null);

        expect(props.course).toEqual({});
    });

    it('sets the authors formatted for a drop down list', () => {
        const courses = [];
        const authors = [ { id: "1", name: "John" }, { id: "2", name: "Jill" }];

        const props = setup(courses, authors, null);

        expect(props.authors).toEqual([
            { value: "1", text: "John" },
            { value: "2", text: "Jill" }
        ])
    });
});
рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

jbri7357 picture jbri7357  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

amorphius picture amorphius  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

jimbolla picture jimbolla  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

rui-ktei picture rui-ktei  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

mickeyreiss-visor picture mickeyreiss-visor  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ