• Работа web-студии с конструктором - реально?

    FinGanapre
    @FinGanapre
    Разработка это не что-то абстрактное, что живёт в вакууме. Цель любого приложения - удовлетворить потребности бизнеса, дать возможность зарабатывать и зарабатывать больше.

    Технологии - просто инструменты, позволяющие решить задачу. Если технология подходит для решения задачи, то она применяется.

    Так что никакой ненависти к вам "настоящие разработчики" не испытывают и сами могут применять подобные инструменты, не переживайте:) Есть разные задачи, которые требуют разных решений и вложений. За счёт этого формируются ниши.

    Есть также подход "потестить идею", когда нужно быстро и с минимальными вложениями посмотреть будет ли то или иное приносить прибыль и уже потом решать нужно ли что-то ещё и можно ли реализовать проект на готовых решениях или для дальнейшего развития необходимо писать своё.
    Ответ написан
    Комментировать
  • Когда использовать useCallback, useMemo и useEffect?

    @LEXA_JA
    useEffect - это хук, который позволяет использовать сайд эффект. В классах его аналогом было использование componentDidMount, componentDidUpdate и componentWillUnmount. В нем можно делать подписки, отправлять запросы управлять анимацией и т. д.
    const [data, setData] = useState(null);
    
    useEffect(() => {
      const controller = new AbortController()
      fetchData(controller.signal).then(setData)
    
      return () => controller.abort()
    }, [fetchData, setData])


    useCallback и useMemo предназначены для оптимизации. useCallback получает на функцию и массив аргументов, и возвращает одну и туже функцию, до тех пор, пока аргументы не изменились. useMemo отличается тем, что он возвращает не саму функцию, а результат её выполнения. По большому счету они являются взаимозаменямыми.
    Таким образом, useMemo используется для сохранения результатов тяжёлых вычислений, например обработка массива.
    const data = useMemo(() => array.map(mapper).filter(predicate).reduce(reducer), [array])

    А useCallback используется, когда важна постоянность ссылок на функцию. Например, когда мы передаём ссылку в компонент, который использует React.PureComponent или React.memo, или, когда функция используется в качестве аргумента в других хуках
    const handler = useCallback(() => {
      // что-то сделать 
    }, [])
    
    useEffect(() => {
      handler(value)
      // если не использовать useCallback, эффект будет срабатывать постоянно 
    }, [handler, value])
    Ответ написан
    1 комментарий
  • Как использовать useStore, useSelector, useDispatch hook?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    1. useDispatch - получение функции store.dispatch в компоненте. Раньше для вызова action функциональный компонент приходилось оборачивать в вызов connect:
    const Foo = ({ dispatch }) => {
      const handler = useCallback(() => {
        dispatch(action());
      }, []);
    
      return (
        <Bar onClick={handler} />
      );
    };
    
    export default connect()(Foo);


    Сейчас:
    const Foo = () => {
      const dispatch = useDispatch();
    
      const handler = useCallback(() => {
        dispatch(action());
      }, []);
    
      return (
        <Bar onClick={handler} />
      );
    };
    
    export default Foo;


    2. useSelector - маппинг значения из store.
    Раньше:
    const Foo = ({ value }) => {
      return (
        <Bar value={value} />
      );
    };
    
    const mapStateToProps = state => ({
      value: state.value,
    });
    
    export default connect(mapStateToProps)(Foo);


    const Foo = () => {
      const value = useSelector(state => state.value);
    
      return (
        <Bar value={value} />
      );
    };
    
    export default Foo;


    3. useStore - получение store целиком:
    const valueSelector = state => state.value;
    
    const Foo = () => {
      const { dispatch, getState, subscribe } = useStore();
      const value = valueSelector(getState());
      
      useEffect(() => subscribe(console.log), []);
    
      const handler = useCallback(() => {
        dispatch(action());
      }, []);
    
      return (
        <Bar onClick={handler} value={value} />
      );
    };
    
    export default Foo;

    Вряд ли useStore вам, действительно, понадобится на практике.
    Ответ написан
    Комментировать
  • TypeScript. Как собрать объект из массива строк?

    @StockholmSyndrome
    getStyles<T extends keyof CSSStyleDeclaration>(styles: Array<T>): Record<T, string> {
        return styles.reduce((res, style) => {
            res[style] = this.$nativeElement.style[style];
            return res;
        }, {} as Record<T, string>);
    }

    тогда выходной объект будет содержать только те свойства, которые были в styles
    Ответ написан
    Комментировать
  • TypeScript. Как собрать объект из массива строк?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    На самом деле чтоб была полноценная типизация надо как-то так:
    getStyles<T extends keyof CSSStyleDeclaration>(styles: readonly T[]) {
      return styles.reduce((res, style) => {
        res[style] = this.$nativeElement.style[style];
        return res;
      }, {} as Pick<CSSStyleDeclaration, T>);
    }

    Песочница.

    Разница с решением от Дмитрий в том, что тип получившегося объекта будет содержать только полученные ключи, а не все возможные в CSSStyleDeclaration.
    Ответ написан
    2 комментария
  • TypeScript. Как затипизировать this?

    @forspamonly2
    надо не this типизировать, а имя метода вместо string делать keyof this.

    abstract class BaseComp {
        abstract listeners: readonly (keyof this)[];
        attachListeners() {
            this.listeners.forEach(name => {
                const method = this[name];
                if(typeof method == 'function') {
                    this[name] = method.bind(this);
                    // attach
                }
            })
        }
        onError() {}
    }
    class Comp extends BaseComp {
        listeners = ["onClick", "onError"] as const;
        onClick(){}
    }


    правда так оно позволит вместо имени метода имя проперти подсунуть. а с фильтром на условном типе отсюда придётся тип класса потомка передавать явно (выводить оно его не хочет). но зато кроме имён методов ничего передать будет нельзя:

    type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T]; 
    function attachListeners<T extends BaseComp2>(component:T, listeners: FunctionPropertyNames<T>[]) {
        listeners.forEach(name => {
            const method = component[name];
            if(typeof method == 'function') {
                component[name] = method.bind(component);
                // attach
            }
        })
    }
    
    abstract class BaseComp2 {
        onError() {}
    }
    
    class Comp2 extends BaseComp2 {
        constructor() {
            super();
            attachListeners<Comp2>(this,["onCLick", "onError"]);
        }
        onCLick(){}
    }
    Ответ написан
    Комментировать
  • TypeScript. Как затипизировать this?

    profesor08
    @profesor08
    Задача, мягко говоря, так себе. Много подводных камней и тд. Однако можно реализовать с оговорками. Методы onClick, onInput и тд., описаны в специальном интерфейсе, его можно имплементировать, или как-то использовать. Но, во первых, тебе придется играться с этими наименованиями, чтоб как-то избежать переопределения этих свойств из разных объектов. Во вторых, это делать плохо и ненадежно. Для всего этого придумали addEventListener. По этому, можно посмотреть какой у него интерфейс, и на основе него построить свои классы так, чтоб можно было создавать какие угодно классы, с нужными ивентами. Но в них не должно быть ничего лишнего, кроме ивентов.

    TypeScript Playground

    interface Object {
      entries<T>(o: { [s: string]: T } | ArrayLike<T>): [string, T][];
    }
    
    type TEventType = keyof HTMLElementEventMap;
    
    type TEventCallback = (this: HTMLElement, ev: HTMLElementEventMap[TEventType]) => any;
    
    type IListener = {
      [key in TEventType]?: TEventCallback;
    };
    
    class ListenerA implements IListener {
      public click() {
        console.log("ListenerA.click");
      }
    
      public mousemove() {
        console.log("ListenerA.mousemove");
      }
    }
    
    class ListenerB implements IListener {
      public click() {
        console.log("ListenerB.click");
      }
    }
    
    class DocumentListener {
      constructor(...components: IListener[]) {
        for (const component of components) {
          const proto = Reflect.getPrototypeOf(component);
          const events = Reflect.ownKeys(proto)
            .filter(key => key !== "contructor") as (keyof IListener)[];
    
          for (const event of events) {
            const callback = component[event];
            if (callback !== undefined) {
              document.body.addEventListener(event, callback);
            }
          }
        }
      }
    }
    
    const dl = new DocumentListener(new ListenerA(), new ListenerB());


    P.S. Крайне не рекомендую заниматься такой фигней. Есть более удобные способы навешивания событий, при том понятные, легко-поддерживаемые.

    type TEventType = keyof HTMLElementEventMap;
    
    type TEventCallback = (this: HTMLElement, ev: HTMLElementEventMap[TEventType]) => any;
    
    type IEvent = {
      event: TEventType;
      callback: TEventCallback;
    }
    
    interface IListener {
      events: IEvent[];
      on(event: TEventType, callback: TEventCallback): any;
    };
    
    class Listener implements IListener {
      events: IEvent[] = [];
    
      on(event: TEventType, callback: TEventCallback) {
        this.events.push({event, callback});
      }
    }
    
    class DocumentListener {
      constructor(...components: IListener[]) {
        for (const component of components) {
          for (const { event, callback } of component.events) {
            document.body.addEventListener(event, callback);
          }
        }
      }
    }
    
    const l1 = new Listener();
    
    l1.on("click", () => {
      console.log("l1.click");
    });
    
    const l2 = new Listener();
    
    l2.on("click", () => {
      console.log("l2.click");
    });
    
    const dl = new DocumentListener(l1, l2);
    Ответ написан
    Комментировать