Categories
Modern JavaScript

Better JavaScript — Loops and Arrays

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at ways to improve our JavaScript code.

Don’t Modify an Object During Enumeration

We shouldn’t modify an object during enumeration.

The for-in loop isn’t required to keep current with object modifications.

So we may get items that are outdated in the loop.

This means that we should rely on the for-in loop to behave predictably if we change the object being modified.

So we shouldn’t have code like:

const dict = {
  james: 33,
  bob: 22,
  mary: 41
}

for (const name in dict) {
  delete dict.bob;
}

We have the dict object but we modified it within the for-in loop.

But the loop isn’t required to get the latest changes, so bob might still show up.

Prefer for-of Loops to for-in Loops for Array Iteration

The for-in loop isn’t meant to be used to iterate through arrays.

The order is unpredictable and we get the keys of the item instead of the item itself.

So if we have something like:

const scores = [4, 4, 5, 7, 7, 3, 6, 6];
let total = 0;
for (const score in scores) {
  total += score;
}
const mean = total / scores.length;

score would be the key of the array.

So we wouldn’t be adding up the scores.

Also, the key would be a string, so we would be concatenating the key strings instead of adding.

Instead, we should use the for-of loop to loop through an array.

For instance, we can write:

const scores = [4, 4, 5, 7, 7, 3, 6, 6];
let total = 0;
for (const score of scores) {
  total += score;
}
const mean = total / scores.length;

With the for-of loop, we get the entries of the array or any other iterable object so that we actually get the numbers.

Prefer Iteration Methods to Loops

We should use array methods for manipulating array entries instead of loops whenever possible.

For instance, we can use the map method to map entries to an array.

We can write:

const inputs = ['foo ', ' bar', 'baz ']
const trimmed = inputs.map((s) => {
  return s.trim();
});

We called map with a callback to trim the whitespace from each string.

This is much shorter than using a loop like:

const inputs = ['foo ', ' bar', 'baz ']
const trimmed = [];
for (const s of inputs) {
  trimmed.push(s.trim());
}

We have to write more lines of code to do the trimming and push it to the trimmed array.

There’re many other methods like filter , reduce , reduceRight , some , every , etc. that we can use to simplify our code.

Conclusion

We shouldn’t modify objects during enumeration.

Also, we should prefer iteration methods to loops.

The for-of is better than the for-in loop for iteration.

Categories
Modern JavaScript

Best of Modern JavaScript — Module Details

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the design of the ES6 module system.

Use a Variable to Specify from which Module I want to Import

We can specify which module to import with the import function.

It takes a string with the path to the module.

For instance, we can write:

(async () => {
  const foo = await import("./foo");
  //...
})();

to import a module with the import function.

It takes a string so we can pass in a string generated dynamically.

It returns a promise so we use await to get the resolved value.

Import a Module Conditionally or On-demand

With the import function, we can import a function conditionally or on-demand.

For instance, we can write:

(async () => {
  if (Math.random() < 0.5) {
    const foo = await import("./foo");
    //...
  }
})();

to import a module conditionally.

Using Variables with Import Statements

We can’t use variables with our import statements.

So we can’t write something like:

import foo from 'bar-' + SUFFIX;

But with the import function, we can write:

(async () => {
  if (Math.random() < 0.5) {
    const foo = await import(`bar-${SUFFIX}`);
    //...
  }
})();

Use Destructuring in an import Statement

We can’t use nested destructuring in an import statement.

This makes sense because exports can only be done at the top level.

It looks like destructuring but the syntax is different.

Imports are static and views on exports.

So we can’t write something like:

import { foo: { bar } } from 'some_module';

Named Exports

With named exports, we can enforce a static structure with objects.

If we create a default export with an object, then we can’t statically analyze the object.

The object can have any properties and they can be nested.

eval() the Code of a Module

We can’t call eval on module code.

Modules are too high level for eval .

eval accepts scripts which doesn’t allow the import or export keywords.

Benefits of ES6 Modules

ES6 modules come with several benefits.

They include a more compact syntax.

Static module structure also helps with eliminating dead code, static checks, optimizations, etc.

Also, we check for cyclic dependencies.

With a standard module system, we eliminate the fragmentation of multiple module systems.

Everything using old module systems will migrate to ES6 standard modules.

Also, we don’t have to use objects as namespaces anymore.

This functionality is now provided by modules.

Objects like Math and JSON serve as namespaces for segregating entities.

Conclusion

ES6 modules provide us with many benefits over older non-standard module systems.

Also, they can be dynamically imported.

They allow for various optimizations.

Categories
React

Framer Motion — MotionValues

With the Framer Motion library, we can render animations in our React app easily.

In this article, we’ll take a look at how to get started with Framer Motion.

Injecting MotionValues

We can inject MotionValues into our components.

They’ll be reflected in all the components.

For example, we can write:

import { motion, useMotionValue } from "framer-motion";
import React from "react";

export default function App() {
  const x = useMotionValue(0);

  return (
    <>
      <motion.div
        drag="x"
        style={{ x, backgroundColor: "red", width: 50, height: 50 }}
      />
      <motion.svg drag="x">
        <motion.circle cx={x} cy="30" r="20" stroke-width="3" fill="red" />
      </motion.svg>
    </>
  );
}

We add the motion.div and motion.svg to set the x position when we drag on both the div and the circle.

We set the x property in the style prop for HTML elements.

For SVGs, we set x as the value of the attribute.

We can watch the latest value of a MotionValue with the onChange method.

For instance, we can write:

import { motion, useMotionValue } from "framer-motion";
import React, { useEffect } from "react";

export default function App() {
  const x = useMotionValue(0);

  useEffect(
    () =>
      x.onChange((latest) => {
        console.log(latest);
      }),
    []
  );

  return (
    <>
      <motion.div
        drag="x"
        style={{ x, backgroundColor: "red", width: 50, height: 50 }}
      />
    </>
  );
}

To call x.onChange in the useEffect callback.

And we get the latest value of x with the latest parameter.

Creating Child MotionValues

We can create MotionValues that are derived from other motion values.

To do this, we can use the useTransform hook:

import { motion, useMotionValue, useTransform } from "framer-motion";
import React from "react";

export default function App() {
  const x = useMotionValue(0);
  const y = useTransform(x, (latest) => latest * 2);

  return (
    <>
      <motion.div
        drag
        style={{ x, y, backgroundColor: "red", width: 50, height: 50 }}
      />
    </>
  );
}

We create the y MotionValue from the x MotionValue by multiplying it by 2.

And then we pass them both into the style prop.

Now the square will move diagonally when we drag it.

We can also pass in an array of values into the useTransform hook to map the input value to the output value.

For instance, we can write:

import { motion, useMotionValue, useTransform } from "framer-motion";
import React from "react";
const xInput = [-300, 0, 300];
const opacityOutput = [0, 1, 0];
const colorOutput = ["#f00", "#fff", "#0f0"];
export default function App() {
  const x = useMotionValue(0);

const opacity = useTransform(x, xInput, opacityOutput);
  const color = useTransform(x, xInput, colorOutput);

  return (
    <>
      <motion.div
        drag="x"
        style={{
          x,
          opacity,
          color,
          width: 100,
          height: 100,
          backgroundColor: "red"
        }}
      >
        hello
      </motion.div>
    </>
  );
}

We specify the MotionValue to map from in the first argument.

The 2nd argument has the range of the x values.

And the 3rd argument is the values that the values in the 2nd argument map to.

Then we apply the values by passing them into the style prop.

And we see the text color and opacity change in the div.

Conclusion

We can use MotionValues to style our animations with Framer Motion.

Categories
React

Framer Motion — Drag Events and MotionValues

With the Framer Motion library, we can render animations in our React app easily.

In this article, we’ll take a look at how to get started with Framer Motion.

Drag Events

We can listen for drag events with the onDrag , onDragStart , onDragEnd , onDirectionLock events.

For example, we can write:

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <>
      <motion.div
        style={{ backgroundColor: "red", width: 100, height: 100 }}
        drag="x"
        onDrag={(event, info) => console.log(info.point.x, info.point.y)}
        onDragStart={(event, info) => console.log(info.point.x, info.point.y)}
        onDragEnd={(event, info) => console.log(info.point.x, info.point.y)}
      />
    </>
  );
}

to let us listen to the drag events.

info.point.x and info.point.y have the x and y coordinates of the drag position.

We can also add the onDirectionLock event to listen for drag direction locks.

For example, we can write:

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <>
      <motion.div
        style={{ backgroundColor: "red", width: 100, height: 100 }}
        drag="x"
        dragDirectionLock
        onDirectionLock={(axis) => console.log(axis)}
        onDrag={(event, info) => console.log(info.point.x, info.point.y)}
        onDragStart={(event, info) => console.log(info.point.x, info.point.y)}
        onDragEnd={(event, info) => console.log(info.point.x, info.point.y)}
      />
    </>
  );
}

We set the dragDirectionLock prop to true and the drag prop to 'x' , so the axis parameter will be set to 'x' .

MotionValue

MotionValues track the state and velocity of animating values.

We can create MotionValues manually to set and get their state, pass multiple components to synchronize motion across components, chain MotionValues with the useTransform hook, and update visual properties without re-rendering.

For example, we can use them to change the opacity of a div when we drag it by writing:

import { motion, useMotionValue, useTransform } from "framer-motion";
import React from "react";

const input = [-200, 0, 200];
const output = [0, 1, 0];

export default function App() {
  const x = useMotionValue(0);
  const opacity = useTransform(x, input, output);

  return (
    <motion.div
      drag="x"
      style={{ x, opacity, backgroundColor: "red", width: 100, height: 100 }}
    />
  );
}

The useMotionValue hook is used to create the MotionValue.

Then we use the useTransform value to map the x value to the input and output .

The input array has the position range in an array.

The output array has the opacity range.

So the position is changed when we drag, and that returns the opacity of the div.

We can get and set MotionValues.

The set method sets the MotionValue.

And the get and getVelocity methods gets the position and velocity respectively.

For example, we can write:

import { motion, useMotionValue, useTransform } from "framer-motion";
import React, { useEffect } from "react";

const input = [-200, 0, 200];
const output = [0, 1, 0];

export default function App() {
  const x = useMotionValue(0);
  const opacity = useTransform(x, input, output);

  useEffect(() => {
    x.set(100);
  }, []);

  useEffect(() => {
    console.log(x.get());
    console.log(x.getVelocity());
  }, [x]);

  return (
    <motion.div
      drag="x"
      style={{ x, opacity, backgroundColor: "red", width: 100, height: 100 }}
    />
  );
}

to set the position with set and get the position and velocity with get and getVelocity .

Conclusion

We can get and set MotionValue values and handle drag events with Framer Motion.

Categories
React

Framer Motion — Drag Animation

With the Framer Motion library, we can render animations in our React app easily.

In this article, we’ll take a look at how to get started with Framer Motion.

Drag and Layout Animations

We can animate our elements when we drag it.

For example, we can write:

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <motion.div
      style={{ backgroundColor: "red", width: 100, height: 100 }}
      drag="x"
      dragConstraints={{ left: 0, right: 300 }}
    />
  );
}

to add a div and then make it draggable.

We make it draggable by setting the drag prop to 'x' to let us drag horizontally.

The dragConstraints prop lets us set the limits of the dragging.

Also, we can set the drag limit to the bounds of another element.

For instance, we can write:

import { motion } from "framer-motion";
import React, { useRef } from "react";

export default function App() {
  const constraintsRef = useRef(null);

  return (
    <motion.div
      ref={constraintsRef}
      style={{ backgroundColor: "green", width: 200, height: 200 }}
    >
      <motion.div
        style={{ backgroundColor: "red", width: 100, height: 100 }}
        drag
        dragConstraints={constraintsRef}
      />
    </motion.div>
  );
}

to set the drag limit to the bound of the outer div.

We can also set the degree of movement allowed outside the constraints.

For example, we can write:

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <motion.div
      drag
      dragConstraints={{ left: 0, right: 300 }}
      dragElastic={0.2}
      style={{ backgroundColor: "red", width: 100, height: 100 }}
    />
  );
}

We set the dragElastic prop to set the amount of movement allowed outside the drag constraints.

The dragMomentum prop lets us apply momentum from the pan gesture of the component while dragging finishes.

It’s true by default.

We can use it by writing:

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <motion.div
      drag
      dragConstraints={{ left: 0, right: 300 }}
      dragMomentum={false}
      style={{ backgroundColor: "red", width: 100, height: 100 }}
    />
  );
}

to remove the extra movement after we finish dragging.

The dragPropagation prop lets us allow drag gestures to propagate to child components.

For example, we can write:

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <motion.div
      drag="x"
      style={{ backgroundColor: "red", width: 100, height: 100 }}
    >
      <motion.div
        drag="x"
        dragConstraints={{ left: 0, right: 300 }}
        dragPropagation
        style={{
          backgroundColor: "green",
          width: 50,
          height: 50
        }}
      ></motion.div>
    </motion.div>
  );
}

Then when we drag the child div, the parent div will also move.

We can control dragging with the useDragControls hook.

For example, we can write:

import { motion, useDragControls } from "framer-motion";
import React from "react";

export default function App() {
  const dragControls = useDragControls();

  function startDrag(event) {
    dragControls.start(event, { snapToCursor: true });
  }

  return (
    <>
      <div onPointerDown={startDrag}>click to drag</div>
      <motion.div
        style={{ backgroundColor: "red", width: 100, height: 100 }}
        drag="x"
        dragControls={dragControls}
      />
    </>
  );
}

We set the onPointerDown prop to the starDrag function.

startDrag calls the dragControls.start method to move the red div.

snapToCursor set to true means that the div will move to align to where the cursor is.

So when we click on the 'click to drag' text, the div will move toward the cursor.

Conclusion

We can animate when we drag an element with Framer Motion.