Giter VIP home page Giter VIP logo

linkshoter's Introduction

Run

yarn
yarn start

Basic conceptions

The project was bootstrapped with Create React App. For real world, it should be some custom setup, but for quick test exercise, it's a good one.

App styled with Styled Components. State managed by Redux. Async data flow handled with Redux Observable.

Components

Components used like low-level styling constructs. All reusable components located in components/common.

import styled from 'styled-components';

const Flex = styled.div`
  display: flex;
  flex-direction: ${props => props.layout};
  justify-content: ${props => props.justify};
  align-items: ${props => props.align};
`;

Flex.defaultProps = {
  justify: 'flex-start',
  align: 'stretch',
  layout: 'column',
};

export default Flex;

Flex component is a good example. I reuse it across all project. Also it could be customized with props.

// Children will be aligned in row direction
<Flex layout="row">
  <Child />
  <Child />
</Flex>

// Children will be aligned in column direction
<Flex layout="column">
  <Child />
  <Child />
</Flex>

Common components don't fully customizable as they could be and options mostly depend on my needs and I don't touch all of them, but I'm not trying to create UI components library.

In theme.js I defined all common variables like fonts, colors etc. There is example how to use it in components.

import styled from 'styled-components';

export default styled.p`
  font-weight: ${props => props.theme.font.fontWeightLight};
`;

All other components are the composition of common components with small customization based on the mockup.

const StyledTitle = styled(Title)`
  margin-right: ${props => props.theme.calcGrid(2)};
`;

const ShortHistoryHeader = props => (
  <Flex layout="row" justify="flex-start" align="center">
    <StyledTitle>
      Previously shortened by you
    </StyledTitle>

    <Action
      onClick={props.handleActionClick}
    >
      Clear history
    </Action>
  </Flex>
);

Big components like ShortHistory component broken down in smaller.

App state

App has one point of state โ€” data from shortening API.

state

Entities reducer is a general store for normalized data from API. Data stored as it comes from API, then builded to list of links by Entities selector, and finally in components became human readable(date formatting).

Side effects handling

There are two side effects points: saving history to localStorage and API calls. History epic saves Links on every action which mean that there are new links in state. Links epic and Stats epic download data from API.

Important point is that epics don't deal directly with localStorage and API. All this logic moved to services in tools and passed to epics as dependencies.

export const statsByLink =
  (action$, store, { apiService: { getLinkStats }, responseParseService }) =>
    action$.ofType(SHORT_LINK_SUCCESS)
      .mergeMap(action => getLinkStats(action.shortcode)
        .map(response => response.status === 200
          ? shortcodeStatsSuccess(
            responseParseService.parseLinkStats(response),
            action.shortcode,
          )
          : shortcodeStatsFail(),
        ),
      );

Staff like parsing response also moved to dependencies, so epic doesn't matter how to get and parse data. Epic only deal with the data flow.

For example, if I will decide change storage from localStorage to database on the backend I will change only one place โ€” implementation of History service.

Conclusion

My main goal was to create the app based on the composition of small single responsible blocks grouped in modules. Some staff not implemented like bundle size optimization, caching API requests and tests(I made the small test for Entities selector, but in the real world epics also should be covered with tests).

Another point to improve is config. Now config variables just hard coded in the file, but in the real world, they should depend on the specific environment because usually API domains and etc different on dev, test and production environments. It could be done via environment variables or injected into index.html if we need to change them on the fly.

Btw, the app doesn't have production mode and only operates in dev environment.

linkshoter's People

Contributors

egor-sapronov avatar

Watchers

 avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.