⚛️ Focused Styling in React and refactoring of Mobx
Are you using React on the project with an external theme? Read more theming of for React, some hints for mobx without decorators and more.
This article is for anyone who is
- working with React
- is planning to start working with React in the near future
- likes practical examples ❤️
- uses Mobx
Styling
Unqualified styling can create the worst technical debt possible. Try to write a minimum of new styles as possible.
- 🚨style only using class name selectors!
- 🚧do not nest selectors!
- 🚨use class names that start with “.js-” for javascript functionality never for styling
Rules for theming and Styling with CSS, SCSS
Mobile first
- Style simple components to take a full width
- Layout components should scale up elements as the viewport changes
- Use only media query based on minimum viewport widths
@media (min-width: ___px) { ... } - In SCSS prefer mixins for media queries from Bootstrap 4
@include media-breakpoint-up(size){...} // is all you need to use
Theming
Employ component library like bootstrap. Use the provided components and make customization exclusively by changing Sass variables.
- Customize Bootstrap or other toolkits, ui-frameworks or ui-libraries
- Rules for HTML in systems with theming
Any code is generally clearer and easier to maintain when it does not use globals, CSS styles are global
Components should own styles
Solution to global styles is to make write styles inside of every component using CSS in the JS library like styled-components or emotion.
Emotion is a library designed for writing CSS styles with JavaScript
Example of a component with styled components in defaultProps
Component design
Thinking in react way. A component should use data to render UI. Design simple components that do only one thing. Components used for Layout and styling should not know about the application model.
function (data) { return UI; }
🏗 Component Categories
🧱 The base styled components
- Elements with styles
💅 Presentational components
- Reusable components with Styles
🎁 Container presenter components
- Customized Presentational Components that know about application Model
🎛 Container components
- Only inject data and behavior to Container presenter components
Categorization of React Components
State management
⚛️React introduced Hooks
Hooks solve state management without an additional library.
Components should not misuse specific state management library
Consider a situation when the creator of mobx will make this project deprecated. If a project has components coupled with mobx, all the components will need to change, compared with only container components have a connection to the state using inject or connect function and the refactorings are much easier.
- decorators make components harder to test
- using store inside of the components requires to mock the whole store for testing
Instead, we should use inject and observer as a functions
https://github.com/mobxjs/mobx-react#inject-as-function
👍 Benefits of using inject and observer functions:
you are not using generators that are not part of the Javascript specification. (🤯 Currently unstable proposal)
- 🚀 the code is future proof, and you do not have to change anything when new generators are released
you can prepare data and actions inside of the inject
- 🍀 you can work with simple, functional components
- ♻️ your components will not know about the store and can be reused
Example:
'; var cookie = '🍪'; // run plugin with config object cc.run({ current_lang : 'sk', // auto_language : 'document', autoclear_cookies : true, // default: false // theme_css: '../src/cookieconsent.css', cookie_name: 'cc_cookie_consent', // default: 'cc_cookie' cookie_expiration : 365, // default: 182 page_scripts: true, // default: false // auto_language: null, // default: null; could also be 'browser' or 'document' // autorun: true, // default: true // delay: 0, // default: 0 // force_consent: false, // hide_from_bots: false, // default: false // remove_cookie_tables: false // default: false // cookie_domain: location.hostname, // default: current domain // cookie_path: "/", // default: root // cookie_same_site: "Lax", // use_rfc_cookie: false, // default: false // revision: 0, // default: 0 gui_options: { consent_modal: { layout: 'cloud', // box,cloud,bar position: 'bottom right', // bottom,middle,top + left,right,center transition: 'slide' // zoom,slide }, settings_modal: { layout: 'bar', // box,bar // position: 'left', // right,left (available only if bar layout selected) transition: 'slide' // zoom,slide } }, onAccept: function (cookie) { console.log('onAccept fired ...'); var file = 'https://www.ableneo.com/plugins/cookies/ajax.php'; var data = new Object(); data.command = 'cookiesConsentChange'; data.cookies = cookie; var target = '#c-info'; ajaxRequestPost(file, JSON.stringify(data), target); // cc.show(); // delete line below // typeof doDemoThings === 'function' && doDemoThings(cookie); // update datalayer consent fn_updateGtagConsent(cc.allowedCategory('analytical'), cc.allowedCategory('commercial')); }, onChange: function (cookie, changed_preferences) { console.log('onChange fired ...'); var file = 'https://www.ableneo.com/plugins/cookies/ajax.php'; var data = new Object(); data.command = 'cookiesConsentChange'; data.cookies = cookie; data.preferences = changed_preferences; var target = '#c-info'; ajaxRequestPost(file, JSON.stringify(data), target); // cc.show(); console.log(changed_preferences); // update datalayer consent fn_updateGtagConsent(cc.allowedCategory('analytical'), cc.allowedCategory('commercial')); /* // If analytics category's status was changed ... if (changed_preferences.indexOf('analytical') > -1) { // If analytics category is disabled ... if (!cc.allowedCategory('analytical')) { // Disable gtag ... console.log('disabling gtag') window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('consent', 'default', { 'ad_storage': 'denied', 'analytics_storage': 'denied' }); } } */ // delete line below // typeof doDemoThings === 'function' && doDemoThings(cookie); }, languages: { 'sk': { consent_modal: { title: 'We use cookies', description: 'We use cookies and other technologies to improve your browsing experience on our website, to show you personalized content and targeted ads, to analyze our website traffic, and to understand where our visitors are coming from.' + '', primary_btn: { text: 'Accept all', role: 'accept_all' // 'accept_selected' or 'accept_all' }/*, secondary_btn: { text: 'Nechcem lepší zážitok', role: 'accept_necessary' // 'settings' or 'accept_necessary' }*/ }, settings_modal: { title: logo, save_settings_btn: 'Save settings', accept_all_btn: 'Accept all', /* reject_all_btn: 'Odmietnuť', */ /* close_btn_label: 'Close', */ cookie_table_headers: [ {col1: 'Name'}, {col2: 'Domain'}, {col3: 'Expiration'}, {col4: 'Description'} ], blocks: [ { title: 'Your privacy is important to us', description: 'Cookies are very small text files that are stored on your computer when you visit a website. We use cookies for a variety of purposes and to enhance your online experience on our website.' + '' + 'You can change your preferences and decline certain types of cookies to be stored on your computer while browsing our website. You can also remove any cookies already stored on your computer, but keep in mind that deleting cookies may prevent you from using parts of our website.' }, { title: 'Technical cookies', description: 'These cookies are essential to provide you with services available through our website and to enable you to use certain features of our website. Without these cookies, we cannot provide you certain services on our website.', toggle: { value: 'technical', enabled: true, readonly: true // cookie categories with readonly=true are all treated as "necessary cookies" } } , { title: 'Analytical cookies', description: 'These cookies are used to collect information to analyze the traffic to our website and how visitors are using our website. For example, these cookies may track things such as how long you spend on the website or the pages you visit which helps us to understand how we can improve our website site for you. The information collected through these tracking and performance cookies do not identify any individual visitor.', toggle: { value: 'analytical', // there are no default categories => you specify them enabled: false, readonly: false } } , { title: 'Marketing cookies', description: 'These cookies are used to show advertising that is likely to be of interest to you based on your browsing habits. If you choose to remove or disable these targeting or advertising cookies, you will still see adverts but they may not be relevant to you.', toggle: { value: 'commercial', enabled: false, readonly: false } } , { title: '', description: '', } ] } } } }); // this custom fix moves the "settings" button to the right, in place of the "cancel all" button (which has been removed in settings above $('#cc_div button[data-cc="c-settings"]').removeClass('cc-link').addClass('c-bn').addClass('c_link').appendTo('#c-bns'); });