HowToCode - Adaptando uma abordagem de sistemas ao desenvolvimento para React e TypeScript

Provavelmente, todo programador, mais cedo ou mais tarde, começa a pensar na qualidade de seu código. E, muito provavelmente, não me enganarei se disser que uma boa metade dos desenvolvedores está sempre insatisfeita com eles. Eu também raramente gostei do meu código: as funções, ao que parecia, poderiam ser encurtadas, seria bom remover aninhamentos desnecessários também. Seria ótimo escrever testes e documentação, mas quase nunca tive tempo para eles.





Naturalmente, passei muito tempo lendo livros e todos os tipos de artigos, tentando descobrir como tornar meu código melhor. Mas a imagem não batia. Ou as recomendações em livros ou artigos eram muito gerais e às vezes contraditórias, ou era minha, mas, apesar dos esforços, houve pouco resultado.





A situação mudou radicalmente depois que fiz o curso HowToCode [link removido pelo moderador, pois quebra as regras] . O curso descreve uma abordagem sistemática e, como tudo brilhante, simples e bonita para o desenvolvimento, que reúne análise, design, documentação, teste e desenvolvimento de código. Todo o curso é construído sobre o uso do paradigma funcional e da linguagem Scheme (um dialeto Lisp), porém, as recomendações são bastante aplicáveis ​​para outras linguagens, e para JavaScript e TypeScript, aos quais tentei adaptá-las, geralmente são multar.





Gostei muito dos resultados:





  • Primeiro, finalmente meu código ficou legível, documentação e testes claros apareceram.





  • Em segundo lugar, a abordagem TDD funcionou, para a qual fiz várias abordagens, mas não consegui começar a segui-la de forma alguma





  • -, : , , ,





  • : - , , -





  • , , , - , , . - .





, .





, , - .





, , .





3 :

















, , , , .





. : , Jira . .





, 2 : . , - . , - , , .





, , . .





:





1.





2. , .





1.

, :









  • : , , - -









, . - , , , , - , .





, :  " web- , , . ."





, , . , , , ( ).









, , - . :









, , , . :





, , , . , - :





, , - . 





2. ,

: 3 , : , .









- , .  , , :





  • : , , , , ..





  • , , ,





  • : , ..









, . :





  • ,





  • ,









, , :









- . , - , . :













  • , ..









: ? , , , , , - .





:





  • :





  • , , , , ,





  • , , ,





  • , , .





, :









, - , . , , .





:





  • : , , , ..





  • :









  • ..





, , :













, .





:





  1. -,





  2. -, ,





  3. -, , , . , , , .





, . , , , .





- .





, , , . 





?





  1. -, . , , . , , .





  2. -, TypeScript, .





  3. -, , , unit-.





:













  1. - , .





  2. - .





. TypeScript JSDoc, - . - React JS, , (props) (state), React. , , , .





: HowToCode , - , . , , .





, .





  , , Redux, .





. . , AppState - :





export interface AppState {}
      
      



. , , , . :

































title













+













backendAddress













+













isLoading













+





true -





false -









group









Group





-













loadData













+









:





export interface AppState {    
    title: string;    
    backendAddress: string;
    isLoading: boolean;
    group?: Group;
    loadData: Function;
}
      
      







TypeScript , , , . ,  , .  JavaScript - JSDoc.





/**
 *   
 * @prop title -  
 * @prop backendAddress -  
 * @prop isLoading -    (true - , false - )
 * @prop group -    .     group  
 * @method loadData -   
 */
export interface AppState {
    title: string;
    backendAddress: string;
    isLoading: boolean;
    group?: Group;
    loadData: Function;
}
      
      



AppState Group. , AppState , - , , . . , . .





, , TODO - IDE TODO





/**
 *   
 * @prop title -  
 * @prop backendAddress -  
 * @prop isLoading -    (true - , false - )
 * @prop group -    .     group  
 * @method loadData -   
 */
export interface AppState {
    title: string;
    backendAddress: string;
    isLoading: boolean;
    group?: Group;
    loadData: Function;
}


/**
 *    
*/
//TODO
export interface Group {

}
      
      



, .





, , - . , unit-, , - . , group - , . - :





/**
 *   
 * @prop title -  
 * @prop backendAddress -  
 * @prop isLoading -    (true - , false - )
 * @prop group -    .     group  
 * @method loadData -   
 */
export interface AppState {
    title: string;
    backendAddress: string;
    isLoading: boolean;
    group?: Group;
    loadData: Function;
}

// 1
const appState1: AppState = {
    title: " 1",
    backendAddress: "/view_doc.html",
    isLoading: true,
    group: undefined,
    loadData: () => {}
}

// 2
const appState2: AppState = {
    title: " 2",
    backendAddress: "/view_doc_2.html",
    isLoading: false,
    group: group1, //   
    loadData: () => {}
}


/**
 *    
*/
//TODO
export interface Group {
}

//TODO
const group1 = {}
      
      



, , , - . , , , - , "".





TODO , , , , .





. , , , , - :





export default abstract class AbstractService {
       
    /**
     *       
     * @fires get_group_data -  ,   
     * @returns   
     */
    abstract getGroupData(): Promise<Group>;

}
      
      



, - , , .





, .





, , . 





, - :





  1. . - , :













    • ,





  2. ,





  3. . .





  4. .





  5. , :









    1. (TODO),





,





React - , .





, , - , .





1 -

- , :





  • ,









  • , , .





TypeScript - :





export const getWorkDuration = (worktimeFrom: string, worktimeTo: string): string => {
    return "6 18";
}
      
      



:





  • getWorkDuration - , ,





  • worktimeFrom: string, worktimeTo: string - .





  • : string -





  • return "6 18" - ,





, , - unit - , .





2 : - . :





:





const componentName = (props: PropsType) => { return <h1>componentName</h1> }
      
      



:





class componentName extends React.Component<PropsType, StateType>{

    state = {
        //      
    }

    render() {        
        return <h1>componentName</h1> 
    }

}
      
      



:





  • PropsType -





  • StateType -





- , , .





, App. , , , -. :





interface AppProps {}

export default class App extends Component<AppProps, AppState> {
    state = {
        title: "  ",
        backendAddress: "",
        isLoading: true,
        loadData: this.loadData.bind(this)
    }

    /**
     *     
     */
    //TODO
    loadData() {

    }

    render() {
        return <h1>App</h1>
    }
}
      
      



:





  1. App "", AppProps





  2. AppState , ,





  3. loadData, , TODO,





2 -

, , , . JSDoc, , , .





, , , , , , .  , :





/**
 *           
 *       ,  ,    -    
 * @param worktimeFrom -      : ( 00:00  23:59)
 * @param worktimeTo -      : ( 00:00  23:59)
 * @return           Y?,  6 18  5,    
 */
//TODO
export const getWorkDuration = (worktimeFrom: string, worktimeTo: string): string => {
    return "6 18";
}
      
      



TODO,   , .





- , , , , . , , , , - , - .





3.

, , . - unit-. , :





  • unit- , , - ,





  • unit- , . ,





  • , .





.  , .









" " - , .





, , . "" enum. :





type TrafficLights = "" | "" | "";
      
      



, , TrafficLights :  , , - :





function trafficLightsFunction (trafficLights: TrafficLights) {
    switch (trafficLights) {
        case "":
            ...
        case "":
            ...
        case "":
            ...
    }
}
      
      



. , TrafficLights - , , "..." , . 





, . , , , , 3 . .





, - , .





, - . , - , - .   " " . , , , , . .





:

























1









, .

















1





- 2 (true / false)





- 1 , 0





2









, .

















..





, , , .





, 100- , , 4 :





1 - 25,





26 - 50,





51 - 75,





76 - 100





4 .





3













(0 - 300]









..





:













4









, ,  





? ,





- , , . ,





. . " " - . - , , .





, , -, .





2 :













- , .





5





()





,





"" :





id

















..





2 .  





6









,









,





, :





,





,





, , 2





, .





/**
 *           
 *       ,  ,    -    
 * @param worktimeFrom -      : ( 00:00  23:59)
 * @param worktimeTo -      : ( 00:00  23:59)
 * @return           Y?,  6 18  5,    
 */
//TODO
export const getWorkDuration = (worktimeFrom: string, worktimeTo: string): string => {
    return "6 18";
}
      
      



, . string , - , :, , 00:00 23:59. , . , . 3 -:





  1. ,





  2. , -





  3. -





, . -, , 3 , , , 5













worktimeFrom





worktimeTo









1





worktimeFrom





, ,





"24:00"





,





"18:00"









2





worktimeFrom





,





"18:00"





, ,





"24:00"









3





,





worktimeFrom < worktimeTo





, worktimeTo,





"00:00"





, worktimeFrom, ,





"23:59"





23 59





4





,





worktimeFrom > worktimeTo





, worktimeTo,





"18:49"





, worktimeFrom, ,





"10:49"





16





5





worktimeFrom = worktimeTo





,  ,





"01:32"





,  ,





"01:32"





0





- , : .  . Jest Enzyme - React JS. , :





describe('       ', () => {
    
    it('     ,   0', ()=>{
        const result = getWorkDuration("01:32", "01:32");
        expect(result).toBe("0");
    });
    
    //    
    ...

});
      
      



- : , , . -, , , - enzyme. 





App. :





interface AppProps {}

export default class App extends Component<AppProps, AppState> {
    state = {
        title: "  ",
        backendAddress: "",
        isLoading: true,
        loadData: this.loadData.bind(this)
    }

    /**
     *     
     */
    //TODO
    loadData() {

    }

    render() {
        return <h1>App</h1>
    }
}
      
      



, , :





/**
 *   
 * @prop title -  
 * @prop backendAddress -  
 * @prop isLoading -    (true - , false - )
 * @prop group -    .     group  
 * @method loadData -   
 */
export interface AppState {
    title: string;
    backendAddress: string;
    isLoading: boolean;
    group?: Group;
    loadData: Function;
}
      
      



, :





  • -, , .





  • -, . , group , , - .  , "". 





, , :





  • () 2 ,





  • , .  , : .





, 4 :





  • 2 - , , "" , ,





  • 2 - , ,





, , 2 .





loadData - , , - , - . , , , , , .





- loadData :





import React from 'react';
import Enzyme, { mount, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });

import App from './App';

describe('App', () => {
    test('  App ,   loadData', () => {

        //    loadData
        const loadData = jest.spyOn(App.prototype, 'loadData');
        
        // 
        const wrapper = mount(<App></App>);
        
        //    loadData
        expect(loadData.mock.calls.length).toBe(1);
    });
}
      
      



enzyme , .  :





  1. loadData ,





  2. ( )





  3. ,





- .





test('  ,     ', () => {
        // 
        const wrapper = mount(<App></App>);

        //      
        wrapper.setState({
            title: " 1",
            backendAddress: "/view_doc.html",
            isLoading: true,
            group: undefined,
            loadData: () => {}
        })

        //   
        //    
        expect(wrapper.find('h1').length).toBe(1);
        expect(wrapper.find('h1').text()).toBe(" 1");

        //,    
        expect(wrapper.find(Spinner).length).toBe(1);

        //,     
        expect(wrapper.find(Group).length).toBe(0);
    });
      
      



:









  1. . , . ,









, , 2 , :





  • -





  • . , , .





, , .





:





test('  ,    .   ', () => {
        const wrapper = mount(<App></App>);

        wrapper.setState({
            title: " 2",
            backendAddress: "/view_doc_2.html",
            isLoading: false,
            group: {
                id: "1",
                name: " 1",
                listOfCollaborators: []
            },
            loadData: () => {}
        })

        expect(wrapper.find('h1').length).toBe(1);
        expect(wrapper.find('h1').text()).toBe(" 2");
        expect(wrapper.find(Spinner).length).toBe(0);
        expect(wrapper.find(Group).length).toBe(1);
    });
      
      



, , , , .  , , , .





, , . , .





4 5.

, . , , , . , :





  • -, ,





  • -, , Knowledge Shift ( ). 









. , , , . , , - .





, . :





/**
 *           
 *       ,  ,    -    
 * @param worktimeFrom -      : ( 00:00  23:59)
 * @param worktimeTo -      : ( 00:00  23:59)
 * @return           Y?,  6 18  5,    
 */
//TODO
export const getWorkDuration = (worktimeFrom: string, worktimeTo: string): string => {
    return "6 18";
}
      
      



, , , ":", , :





  1. , , , 00:00





  2. , ,





  3. X Y?,





:





  • ":", ..





  • .





:





/**
 *           
 *       ,  ,    -    
 * @param worktimeFrom -      : ( 00:00  23:59)
 * @param worktimeTo -      : ( 00:00  23:59)
 * @return           Y?,  6 18  5,    
 */
export const getWorkDuration = (worktimeFrom: string, worktimeTo: string): string => {
    const worktimeFromInMinutes = getWorktimeToMinutes(worktimeFrom);
    const worktimeToInMinutes = getWorktimeToMinutes(worktimeTo);
    const minutesDiff = calcDiffBetweenWorktime(worktimeFromInMinutes, worktimeToInMinutes);
    return convertDiffToString(minutesDiff);
}


/**
 * c  ,    
 * @param worktimeFrom -    : ( 00:00  23:59)
 * @returns  ,   00 00
 */
//TODO
export const getWorktimeToMinutes = (worktime: string): number => {
    return 0;
}


/**
 *            
 * @param worktimeFrom -        ,    
 * @param worktimeTo -        ,    
 * @returns           
 */
//TODO
export const calcDiffBetweenWorktime = (worktimeFrom: number, worktimeTo: number): number => {
    return 0;
}


/**
 *         Y?
 * @param minutes -  
 * @returns     Y?,  6 18  5
 */
//TODO
export const convertDiffToString = (minutes: number): string => {
    return "6 18";
}
      
      



, , , . . , TODO, . . , , .





, . , , , , - , .





Knowledge Shift





, , Knowledge Shift.





, , , , , , - () . , , , .





, , - , , . Knowledge Domain Shift Knowledge Shift.





, , , , .





, ,   - App:





export default class App extends Component<AppProps, AppState> {
    state = {
        title: "  ",
        backendAddress: "",
        isLoading: true,
        group: undefined,
        loadData: this.loadData.bind(this)
    }

    /**
     *     
     */
    //TODO
    loadData() {}


    componentDidMount() {
        //  loadData   
        this.loadData();
    }


    render() {
        const {isLoading, group, title} = this.state;
        return (
            <div className="container">
                <h1>{title}</h1>
                {
                    isLoading ?
                    <Spinner/>
                    //  Group     
                    : <Group group={group}></Group>
                }
            </div>
        );
    }
}
      
      



componentDidMount, . render. , , - , Group.





, , c . group , , App - , .  , , , , , , .





, , . , , TODO : + . , TODO .





Quando esse momento maravilhoso chegar, basta iniciar o aplicativo e desfrutar de como ele simplesmente funciona. Ele não falha por causa de erros perdidos ou cenário esquecido e não realizado, mas simplesmente funciona.





Esta é, em geral, toda a abordagem. Não é difícil, mas requer hábito e disciplina. Como em qualquer negócio complicado, o maior desafio é começar e resistir à tentação de parar nos primeiros casais. Se der certo, depois de um tempo você não vai querer nem pensar em como escrever código da maneira antiga: sem testes, documentação e com funções longas e incompreensíveis. Boa sorte!








All Articles