Dependency injection
в React
Мостовой Никита,
@xnimorz
1
2
3
4
Команда архитектуры фронтенда
5
Команда архитектуры фронтенда
6
�
Команда архитектуры фронтенда
7
Команда архитектуры фронтенда
8
Команда архитектуры фронтенда
9
10
11
12
13
14
15
16
17
18
19
20
function SearchPage({ query, searchResults }) {
return (
<Layout>
<Row>
<Column>
<SearchForm query={query.text} />
</Column>
</Row>
<Row>
<Column>
<SearchTabs active={query.kind} />
</Column>
</Row>
21
function SearchPage({ query, searchResults }) {
return (
<Layout>
<Row>
<Column>
<SearchForm query={query.text} />
</Column>
</Row>
<Row>
<Column>
<SearchTabs active={query.kind} />
</Column>
</Row>
22
function SearchPage({ query, searchResults }) {
return (
<Layout>
<Row>
<Column>
<SearchForm query={query.text} />
</Column>
</Row>
<Row>
<Column>
<SearchTabs active={query.kind} />
</Column>
</Row>
23
function SearchPage({ query, searchResults }) {
return (
<Layout>
<Row>
<Column>
<SearchForm query={query.text} />
</Column>
</Row>
<Row>
<Column>
<SearchTabs active={query.kind} />
</Column>
</Row>
24
<Row>
<Column>
<Title>Заголовок</Title>
</Column>
</Row>
...
<SearchResults query={query} searchResults={searchResults}/>
</Layout>
);
}
25
<Row>
<Column>
<Title>Заголовок</Title>
</Column>
</Row>
...
<SearchResults query={query} searchResults={searchResults}/>
</Layout>
);
}
26
function SearchResults({ query, searchResults }) {
return (
<Row>
<Column>
<Clusters query={query} />
</Column>
<Column>
<SearchSettings query={query} />
<Vacancies searchResults={searchResults} />
</Column>
</Row>
);
}
27
function SearchResults({ query, searchResults }) {
return (
<Row>
<Column>
<Clusters query={query} />
</Column>
<Column>
<SearchSettings query={query} />
<Vacancies searchResults={searchResults} />
</Column>
</Row>
);
}
28
function SearchResults({ query, searchResults }) {
return (
<Row>
<Column>
<Clusters query={query} />
</Column>
<Column>
<SearchSettings query={query} />
<Vacancies searchResults={searchResults} />
</Column>
</Row>
);
}
29
30
Page
query
31
Page
Component1
query
query
query
32
Page
Component1
…
query
query
query
query
33
Page
Component1
…
Component100500
query
query
query
query
query
query
34
35
36
⛓ Зацепление (coupling)
37
Page
Component1
…
Component100500
query
query
query
query
query
query
38
39
query
query
query
query
query
query
query
query
query
query
query
query
query
query
query
query
query
query
40
query
query
query
query
query
query
query
query
query
query
query
query
query
query
query
query
query
query
🚌 Композиция
41
<div className="app">
<UserAvatar user={user} />
</div>
42
<div className="app">
<UserAvatar user={user} />
<Body footer={<Footer user={user} />}>
<Content />
</Body>
</div>
43
44
45
Page
Component1
…
Component100500
query
query
query
query
query
query
46
Page
Component1
…
Component100500
query
query
query
query
query
query
Page
Component1
query
47
Page
Component1
…
Component100500
query
query
query
query
query
query
Page
Component1
…
Component100500
query
Component100500 query={query}
query
Component100500 query={query}
🏎 Render function
48
function MyTodoList({ todos }) {
return (� ...
<ul>
{todos.map(item =>
<Todo todo={item} />
)}
</ui>
49
function MyTodoList({ todos }) {
return (
...� <ul>
{todos.map(item =>
<Todo todo={item} />
)}
</ul>
50
function MyTodoList({ todos }) {
return (� ...
<TodoList
render={todo => <Todo todo={todo} />}
/>
);
}
51
function MyTodoList({ todos }) {
return (� ...
<TodoList
render={todo => <Todo todo={todo} />)}
/>
);
}
52
👉 react-final-form
react-router
react-dropzone
react.Context
53
👉 react-final-form
👉 react-router
react-dropzone
react.Context
54
👉 react-final-form
👉 react-router
👉 react-dropzone
react.Context
55
👉 react-final-form
👉 react-router
👉 react-dropzone
👉 react.Context
56
57
Page
Component1
…
Component100500
query
query
query
query
query
query
Page
Component1
…
Component100500
query
Component100500 query={query}
query
Component100500 query={query}
58
Page
Component1
…
Component100500
query
query
query
query
query
query
Page
Component1
…
Component100500
query
query
() => <Component100500 query={query} />
() => <Component100500 … />
59
Page
Component1
…
Component100500
query
query
() => <Component100500 query={query}
() => <Component100500
Page
Component1
…
Component100500
query
Component100500 ...
query
Component100500 query={query}
60
61
🛸 Redux
62
63
64
connect(state => ({
query: state.query
}))(Clusters)
65
connect(state => ({
query: state.query
}))(Clusters)
redux ❤️ context
66
Inversion of Control
67
public class TmsBackofficeMiddleware {
private static final Logger LOGGER = LoggerFactory.getLogger("dms-msg");
private static final ZoneId MOSCOW = ZoneId.of("Europe/Moscow");
private static final Integer BATCH_SIZE = 1000;
@Inject
private TmsBackofficeService tmsBackofficeService;
68
public class TmsBackofficeMiddleware {
private static final Logger LOGGER = LoggerFactory.getLogger("dms-msg");
private static final ZoneId MOSCOW = ZoneId.of("Europe/Moscow");
private static final Integer BATCH_SIZE = 1000;
@Inject
private TmsBackofficeService tmsBackofficeService;
69
Не передаем в конструкторе или сеттером
<A>
<B>
<C>
<D />
</C>
</B>
</A>
70
<A>
<B>
<C>
<D />
</C>
</B>
</A>
connect(({vacancies}) => ({
vacancies,
}))(D)
71
<A>
<B>
<C>
<D />
</C>
</B>
</A>
connect(({vacancies}) => ({
vacancies,
}))(D)
function D({vacancies}) {
console.log(vacancies)
/**
* [
* {
* id: 1,
* name: 'Программист'
* }
* ]
*/
}
72
<A>
<B>
<C>
<D />
</C>
</B>
</A>
connect(({vacancies}) => ({
vacancies,
}))(D)
function D({vacancies}) {
console.log(vacancies)
/**
* [
* {
* id: 1,
* name: 'Программист'
* }
* ]
*/
}
73
Dependency Injection
74
render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector('#root')
);
75
render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector('#root')
);
76
render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector('#root')
);
const store = React.createContext(store);�
render(<App />, document.querySelector('#root'));
77
render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector('#root')
);
const store = React.createContext(store);�
render(<App />, document.querySelector('#root'));
78
79
Page
Component1
…
Component100500
query
query
query
query
query
query
Page
query
Store
80
Page
Component1
…
Component100500
query
query
query
query
query
query
Page
…
query
Store
Component100500
81
Page
Component1
…
Component100500
query
query
query
query
query
query
Page
…
Connect
query
query
Store
Component100500
query
82
Page
Component1
…
Component100500
query
query
query
query
query
query
Page
…
Connect
query
query
Store
Component100500
query
83
Page
Component1
…
Component100500
query
query
query
query
query
query
Page
…
Connect
query
query
Store
Component100500
query
render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector('#root')
);
const store = React.createContext(store);�
render(<App />, document.querySelector('#root'));
84
🚀 Контекст
85
✨ Определяется для всего приложения
✨ Может быть получен из любого компонента
✨ Может быть переопределен для поддерева
86
87
88
89
✨ Определяется для всего приложения
✨ Может быть переопределен для ----поддерева
✨ Может быть получен из любого ----компонента
90
🐣 Использование контекста
91
👉 Иммутабельные данные
92
👉 Иммутабельные данные
👉 Дата-слой приложения
93
const Vacancy = React.createContext();
function VacancyProvider({ children }) {
const [ name, setName ] = useState('Frontend-dev');
const [ id ] = useState(123);
const value = {
data: { id, name },
changeName: (newName) => {
setName(newName);
}
};
return <Vacancy.Provider value={value}> {children} </Vacancy.Provider>;
}
94
const Vacancy = React.createContext();
function VacancyProvider({ children }) {
const [ name, setName ] = useState('Frontend-dev');
const [ id ] = useState(123);
const value = {
data: { id, name },
changeName: (newName) => {
setName(newName);
}
};
return <Vacancy.Provider value={value}> {children} </Vacancy.Provider>;
}
95
const Vacancy = React.createContext();
function VacancyProvider({ children }) {
const [ name, setName ] = useState('Frontend-dev');
const [ id ] = useState(123);
const value = {
data: { id, name },
changeName: (newName) => {
setName(newName);
}
};
return <Vacancy.Provider value={value}> {children} </Vacancy.Provider>;
}
96
const Vacancy = React.createContext();
function VacancyProvider({ children }) {
const [ name, setName ] = useState('Frontend-dev');
const [ id ] = useState(123);
const value = {
data: { id, name },
changeName: (newName) => {
setName(newName);
}
};
return <Vacancy.Provider value={value}> {children} </Vacancy.Provider>;
}
97
const Vacancy = React.createContext();
function VacancyProvider({ children }) {
const [ name, setName ] = useState('Frontend-dev');
const [ id ] = useState(123);
const value = {
data: { id, name },
changeName: (newName) => {
setName(newName);
}
};
return <Vacancy.Provider value={value}> {children} </Vacancy.Provider>;
}
98
const Vacancy = React.createContext();
function VacancyProvider({ children }) {
const [ name, setName ] = useState('Frontend-dev');
const [ id ] = useState(123);
const value = {
data: { id, name },
changeName: (newName) => {
setName(newName);
}
};
return <Vacancy.Provider value={value}> {children} </Vacancy.Provider>;
}
99
🔎 Где здесь dependency injection?
100
⛓ Уменьшаем зацепление
calculateChangedBits
101
102
const Vacancy = React.createContext();
...
function Vacancy() {
const { data } = useContext(Vacancy)
return data.name;
}
103
const Vacancy = React.createContext();
...
function Vacancy() {
const { data } = useContext(Vacancy)
return data.name;
}
⛓ Уменьшаем зацепление
⚙️ Внедряем полноценную модель
104
const Vacancy = React.createContext();
function VacancyProvider({ children }) {
const [ name, setName ] = useState('Frontend-dev');
const [ id ] = useState(123);
const value = {
data: { id, name },
changeName: (newName) => {
setName(newName);
}
};
return <Vacancy.Provider value={value}> {children} </Vacancy.Provider>;
}
105
const Vacancy = React.createContext();
function VacancyProvider({ children }) {
const [ name, setName ] = useState('Frontend-dev');
const [ id ] = useState(123);
const value = {
data: { id, name },
changeName: (newName) => {
setName(newName);
}
};
return <Vacancy.Provider value={value}> {children} </Vacancy.Provider>;
}
106
const Vacancy = React.createContext();
function VacancyProvider({ children }) {
const [ name, setName ] = useState('Frontend-dev');
const [ id ] = useState(123);
const value = {
data: { id, name },
changeName: (newName) => {
setName(newName);
}
};
return <Vacancy.Provider value={value}> {children} </Vacancy.Provider>;
}
107
🛠 Оптимизация контекста
108
109
👉 Мемоизация данных
calculateChangedBits
110
👉 Мемоизация данных
👉 calculateChangedBits
111
🤷🏼♀️ Как контекст не стоит использовать
112
👉 Ссылки на элементы которые нужны во время первого рендера
Данные, которые необходимо проверять в SCU или memo
113
👉 Ссылки на элементы которые нужны во время первого рендера
👉 Данные, которые необходимо проверять в SCU или memo
114
👩🏽💻 Подведем итоги?
115
⛓ Контекст решает проблему зацепления (coupling)
Контекст позволяет инжектить данные и методы
Контекст не работает со стандартными memo и SCU
Для оптимизации лучше использовать мемоизацию или calculateChangedBits
116
⛓ Контекст решает проблему зацепления (coupling)
🛠 Контекст позволяет инжектить данные и методы
Контекст не работает со стандартными memo и SCU
Для оптимизации лучше использовать мемоизацию или calculateChangedBits
117
⛓ Контекст решает проблему зацепления (coupling)
🛠 Контекст позволяет инжектить данные и методы
⚖️ Контекст не работает со стандартными memo и SCU
Для оптимизации лучше использовать мемоизацию или calculateChangedBits
118
⛓ Контекст решает проблему зацепления (coupling)
🛠 Контекст позволяет инжектить данные и методы
⚖️ Контекст не работает со стандартными memo и SCU
⚙️ Для оптимизации лучше использовать мемоизацию или calculateChangedBits
119
120
Ссылка на все ссылки: https://bit.ly/2MO9srZ