#
Scattered
styled-components is a library I really like when writing css for react, after a few days I have researched about its source, I decided to rewrite it to understand as much as possible, I feel like learning this repo so I wrote the article so I shared with everyone what I learned from it, in order to understand the code in my repo, I would say 2 new concepts to me and I must understand, if you guys If you already know, you can skip it ^^
Stylesheet
If anyone knows the stylesheet in javascript, you can skip this paragraph. In the styled-components, it is used to store styles, each style tag has 1 sheet, each sheet has many rules, 1 rule corresponds to className and attached style, eg below illustrate how to add a rule to the sheet
1 2 3 4 5 | // get tất cả các các sheet trong app const sheet = document.styleSheets; //chọn 1 rule trong sheet và add css vào rule sheet[1].insertRule ('.classname {width : 100%;height : 100%;}', indexStyle) |
Detailed link about it: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet
## Tagged templates The tagged templates I mentioned here are syntax that are commonly used in styled-components
1 2 3 4 5 6 7 8 | <span class="token comment">// function này chỉ return 1 array chứ tất cả các đối số của function này</span> <span class="token keyword">const</span> <span class="token function-variable function">templateFunc</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token operator">...</span> args <span class="token punctuation">)</span> <span class="token operator">=></span> args templateFunc <span class="token template-string"><span class="token string">` width : 100%; height : 50%; background : </span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> props <span class="token operator">=></span> props <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">; `</span></span> |
The result is that we get the parameters like this
Detailed link about it: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
## Embark on writing the library I will write the ComponentStyle class to handle the creation of the stylesheet and insert the css for the component
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <span class="token keyword">class</span> <span class="token class-name">ComponentStyle</span> <span class="token punctuation">{</span> sheet <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token function">constructor</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// ngay khi class khởi tạo thì mình sẽ tạo thẻ style và lưu sheet của style đó vào biến sheet trong class</span> <span class="token keyword">const</span> styleDom <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">makeStyleTag</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// lấy sheet và save vào biến this.sheet </span> <span class="token keyword">this</span> <span class="token punctuation">.</span> sheet <span class="token operator">=</span> styleDom <span class="token punctuation">.</span> sheet <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function-variable function">makeStyleTag</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// function này sẽ tạo style và add thẻ style đó vào thẻ head</span> <span class="token keyword">const</span> style <span class="token operator">=</span> document <span class="token punctuation">.</span> <span class="token function">createElement</span> <span class="token punctuation">(</span> <span class="token string">"style"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> style <span class="token punctuation">.</span> <span class="token function">setAttribute</span> <span class="token punctuation">(</span> <span class="token string">"data-style-duc-version"</span> <span class="token punctuation">,</span> <span class="token string">"1.0.0"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> document <span class="token punctuation">.</span> head <span class="token punctuation">.</span> <span class="token function">insertBefore</span> <span class="token punctuation">(</span> style <span class="token punctuation">,</span> document <span class="token punctuation">.</span> head <span class="token punctuation">.</span> childNodes <span class="token punctuation">[</span> document <span class="token punctuation">.</span> head <span class="token punctuation">.</span> childNodes <span class="token punctuation">.</span> length <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> style <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token function">insertBefore</span> <span class="token punctuation">(</span> css <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// function sẽ tạo ra tên các class một cách random cho các component và return về tên class đó</span> <span class="token keyword">const</span> className <span class="token operator">=</span> <span class="token function">uuid</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> newName <span class="token operator">=</span> <span class="token string">"style-duc-"</span> <span class="token operator">+</span> className <span class="token punctuation">.</span> <span class="token function">slice</span> <span class="token punctuation">(</span> <span class="token number">0</span> <span class="token punctuation">,</span> <span class="token number">5</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> sheet <span class="token punctuation">.</span> <span class="token function">insertRule</span> <span class="token punctuation">(</span> <span class="token template-string"><span class="token string">`.</span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> newName <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">{</span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> css <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">}`</span></span> <span class="token punctuation">,</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> sheet <span class="token punctuation">.</span> cssRules <span class="token punctuation">.</span> length <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> newName <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
The code in the core.js file is probably the most ambiguous, I think to understand the most about your code, so I should clone this repo and run it then read here, it will be much easier to understand.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | <span class="token comment">// function này sẽ trực tiếp tạo ra Element React </span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">createStyledComponent</span> <span class="token punctuation">(</span> target <span class="token punctuation">,</span> css <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> WrappedStyledComponent <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token comment">// khởi taọ class ComponentStyle</span> <span class="token keyword">const</span> componentStyle <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ComponentStyle</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// lưu nó vào trong biến WrappedStyledComponent, để tí nữa ở func useStyledComponentImpl sẽ lôi ra dùng</span> WrappedStyledComponent <span class="token punctuation">.</span> componentStyle <span class="token operator">=</span> componentStyle <span class="token punctuation">;</span> WrappedStyledComponent <span class="token punctuation">.</span> target <span class="token operator">=</span> target <span class="token punctuation">;</span> <span class="token comment">// eslint-disable-next-line react-hooks/rules-of-hooks</span> <span class="token keyword">const</span> <span class="token function-variable function">forwardRef</span> <span class="token operator">=</span> <span class="token punctuation">(</span> props <span class="token punctuation">,</span> ref <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">useStyledComponentImpl</span> <span class="token punctuation">(</span> WrappedStyledComponent <span class="token punctuation">,</span> props <span class="token punctuation">,</span> ref <span class="token punctuation">,</span> css <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> Element <span class="token operator">=</span> React <span class="token punctuation">.</span> <span class="token function">forwardRef</span> <span class="token punctuation">(</span> forwardRef <span class="token punctuation">)</span> <span class="token punctuation">;</span> Element <span class="token punctuation">.</span> <span class="token function-variable function">toString</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token template-string"><span class="token string">`.</span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> WrappedStyledComponent <span class="token punctuation">.</span> newClassToString <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">`</span></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> Element <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> <span class="token function-variable function">renderCss</span> <span class="token operator">=</span> <span class="token punctuation">(</span> cssRaw <span class="token punctuation">,</span> propsElement <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// function này sẽ handle việc map mảng cssRaw thành chuỗi css, mỗi một element trong mảng cssRaw thì có thể function hoặc có thể là string</span> <span class="token keyword">let</span> css <span class="token operator">=</span> <span class="token string">""</span> <span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">const</span> elementCss <span class="token keyword">of</span> cssRaw <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">typeof</span> elementCss <span class="token operator">===</span> <span class="token string">'function'</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> result <span class="token operator">=</span> <span class="token function">elementCss</span> <span class="token punctuation">(</span> propsElement <span class="token punctuation">)</span> <span class="token punctuation">;</span> css <span class="token operator">+=</span> result <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">typeof</span> elementCss <span class="token operator">===</span> <span class="token string">"string"</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> css <span class="token operator">+=</span> elementCss <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> css <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token comment">// CORE FUNCTION </span> <span class="token comment">// function này mình sẽ handle việc tạo ra react component, merge props , truyền ref </span> <span class="token keyword">const</span> <span class="token function-variable function">useStyledComponentImpl</span> <span class="token operator">=</span> <span class="token punctuation">(</span> WrappedStyledComponent <span class="token punctuation">,</span> props <span class="token punctuation">,</span> ref <span class="token punctuation">,</span> css <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// support cho vấn đề ThemeProvider trong styled-componets</span> <span class="token keyword">const</span> theme <span class="token operator">=</span> React <span class="token punctuation">.</span> <span class="token function">useContext</span> <span class="token punctuation">(</span> ThemeContext <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> newProps <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">...</span> props <span class="token punctuation">,</span> <span class="token operator">...</span> <span class="token punctuation">{</span> theme <span class="token punctuation">,</span> ref <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> newCss <span class="token operator">=</span> <span class="token function">renderCss</span> <span class="token punctuation">(</span> css <span class="token punctuation">,</span> newProps <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// insert css và get đc className mới</span> <span class="token keyword">const</span> className <span class="token operator">=</span> WrappedStyledComponent <span class="token punctuation">.</span> componentStyle <span class="token punctuation">.</span> <span class="token function">insertBefore</span> <span class="token punctuation">(</span> newCss <span class="token punctuation">)</span> <span class="token punctuation">;</span> WrappedStyledComponent <span class="token punctuation">.</span> newClassToString <span class="token operator">=</span> className <span class="token punctuation">;</span> <span class="token comment">// nối tên các className</span> newProps <span class="token punctuation">.</span> className <span class="token operator">=</span> <span class="token punctuation">[</span> props <span class="token punctuation">.</span> className <span class="token punctuation">,</span> className <span class="token punctuation">]</span> <span class="token punctuation">.</span> <span class="token function">join</span> <span class="token punctuation">(</span> <span class="token string">" "</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// cuối cùng là tạo react element</span> <span class="token keyword">return</span> React <span class="token punctuation">.</span> <span class="token function">createElement</span> <span class="token punctuation">(</span> WrappedStyledComponent <span class="token punctuation">.</span> target <span class="token punctuation">,</span> newProps <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> |
Finally, the code in the index.js file, is the file that this star will import it whenever using styled-components
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token comment">// domElements là một mảng chứa các thẻ trong html , domElements = ['a,', 'div', 'section','svg', ....], </span> <span class="token comment">//kỹ hơn thì các bạn vào repo của mình xem, vì nó dài nên mình ko trích vào đây</span> <span class="token keyword">import</span> domElements <span class="token keyword">from</span> <span class="token string">'./utils/domElements'</span> <span class="token punctuation">;</span> <span class="token keyword">import</span> createStyledComponent <span class="token keyword">from</span> <span class="token string">'./core'</span> <span class="token keyword">import</span> css <span class="token keyword">from</span> <span class="token string">'./utils/css'</span> <span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> createContext <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span> <span class="token keyword">export</span> <span class="token keyword">const</span> ThemeContext <span class="token operator">=</span> <span class="token function">createContext</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token keyword">export</span> <span class="token keyword">const</span> ThemeProvider <span class="token operator">=</span> ThemeContext <span class="token punctuation">.</span> Provider <span class="token keyword">const</span> <span class="token function-variable function">styled</span> <span class="token operator">=</span> <span class="token punctuation">(</span> tag <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator">...</span> args <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">createStyledComponent</span> <span class="token punctuation">(</span> tag <span class="token punctuation">,</span> <span class="token function">css</span> <span class="token punctuation">(</span> <span class="token operator">...</span> args <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> domElements <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> domElement <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// đoạn này mình gán thể này để tí nữa mọi người có thể dùng như thế này : styled.div` ... `</span> styled <span class="token punctuation">[</span> domElement <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">styled</span> <span class="token punctuation">(</span> domElement <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> styled |
And okay, you can import your fake styled-componets to use like a real library =)))) You can go straight to the sandbox link to see and demo code https://codesandbox.io/embed/github/ducga1998/rewrite-styled-components/tree/master/?fontsize=14&hidenavigation=1&theme=dark
Conclusion
Hope everyone will find it interesting through my article, thank you guys for reading till here:)) Repo: https://github.com/ducga1998/rewrite-styled-components