Loading...

Follow Swizec Teller - A geek with a hat on Feedspot

Continue with Google
Continue with Facebook
or

Valid

People often ask: “Hey can you make a tooltips tutorial? I can’t figure this out”

Pssh yeah I can make a tooltips tutorial. You kiddin me? It’s just a floating square how hard can that be.

And then it was an hour later and I still couldn’t decide just where to put state and whether we should use context or what.

Tooltips … tooltips are hard. So simple in theory so hard to organize into sensible code. Wrecks your mind.

A tooltip tutorial with React hooks

Your goal is to build this:

  1. A tooltip component
  2. Some way to store tooltip position and content
  3. Ability to change that on mouse over

Mousing over a thing – slice of the donut chart in this case – updates positioning and content. This triggers a tooltip re-render. Tooltip shows up where you need saying what you want.

So how do you organize that in a way that makes sense?

You can watch me flounder around trying different solutions in this stream from last night. In the end we went with a combination of state in the App component and React Context shared between everyone else.

ReactVizHoliday Day 7: A piechart and tooltips about christmas stockings - YouTube

We’re using React hooks because hooks are the hot new kid on the block and learning new coding paradigms is fun.

Oh there’s also a codesandbox you can check out:

Or read this online https://reactviz.holiday/gift-stockings/

Click here, if you’d like a daily dataviz tutorial/challenge until Christmas https://reactviz.holiday/

Managing and sharing tooltip state
const [tooltip, setTooltip] = useState({
  show: false,
  x: 0,
  y: 0,
  content: '',
  orientLeft: false,
})

Our tooltip state holds a show flag, tooltip coordinates, and content. orientLeft is the nascent beginnings of a fuller tooltip API. The tooltip component is going to consume this context and use it to render itself.

To make changing this state easier, we sneakily include setTooltip in the object passed into React Context itself.


Now any consumer can change values in context. Whoa

The <Tooltip> component

Our <Tooltip> component doesn’t do much on its own. It’s a wrapper that handles positioning, visibility, and supports a nascent orientation API. We can use orientLeft to align our tooltip left or right. A fuller API would also have top/bottom and a bunch of similar features.

const Tooltip = ({ width, height, children }) => {
  const { x, y, show, orientLeft } = useContext(TooltipContext)

  return (
    
      
        {children}
      
    
  )
}

useContext takes the TooltipContext object and returns its current value on every render. We use destructuring to get at the parts we need: coordinates, show flag, orientation.

Tooltip then renders a <g> grouping element with positioning based on the orientation, and visibility based on the flag. Inside it wraps children in a sized foreignObject element. This allows us to embed HTML inside SVG.

HTML is better for tooltip content than SVG because HTML supports text automatic layouting. Set a width and the browser will figure out what to do with long strings. Don’t get that with SVG.

The Tooltip.js file also exports a React Context.

const TooltipContext = React.createContext({
  show: false,
  x: 0,
  y: 0,
  orientLeft: false,
  content: '',
})

// ...
export default Tooltip
export { TooltipContext }

Makes it easier to share the same context between different consumers.

Render Tooltip in App

Rendering our tooltip happens in the main App component. It also holds tooltip state that gets passed into React Context.

import Tooltip, { TooltipContext } from "./Tooltip";

// ...

function App() {
  const [tooltip, setTooltip] = useState({
    show: false,
    x: 0,
    y: 0,
    content: "",
    orientLeft: false
  });

  return (
      
        
          {* // where you put tooltip triggerers *}

          
            {tooltip.content}
          
        
      
  );
}

We import tooltip and its context, then useState to create a local tooltip state and its setter. Pass both of those in a common object into a <TooltipContext.Provider.

That part took me a while to figure out. Yes with React hooks you still need to render Providers. Hooks are consumer side.

Render our Tooltip as a sibling to all the other SVG stuff. Any components that want to render a tooltip will share the same one. That’s how it usually works.

<TooltipP> is a styled component by the way.

const TooltipP = styled.p`
  background: ${chroma('green')
    .brighten()
    .hex()};
  border-radius: 3px;
  padding: 1em;
`

Nice green background, rounded corners, and a bit of padding.

I am no designer

Trigger tooltips from donuts

Donut code itself is based on code we built for the Will you buy a Christmas tree? donut chart.

We split it into the main donut component and a component for each slice, or <Arc>. Makes it easier to calculate coordinates for tooltips. Means we ca handle slice highlighted state locally in its own component.

const Arc = ({ d, r, color, offsetX, offsetY }) => {
  const [selected, setSelected] = useState(false)
  const tooltipContext = useContext(TooltipContext)

  const arc = d3
    .arc()
    .outerRadius(selected ? r + 10 : r)
    .innerRadius(selected ? r - 80 : r - 75)
    .padAngle(0.01)

  const mouseOver = () => {
    const [x, y] = arc.centroid(d)

    setSelected(true)
    tooltipContext.setTooltip({
      show: d.index !== null,
      x: x + offsetX + 30,
      y: y + offsetY + 30,
      content: d.data.stuffer,
      orientLeft: offsetX  {
    setSelected(null)
    tooltipContext.setTooltip({ show: false })
  }

  return (
    
  )
}

Here you can see a downside of hooks: They can lead to pretty sizeable functions if you aren’t careful.

We create a selected flag and its setter with a useState hook and we hook into our tooltip context with useContext. We’ll be able to use that setTooltip method we added to show a tooltip.

Then we’ve got that const arc stuff. It creates an arc path shape generator. Radius depends on selected status.

All that is followed by our mouse eve handling fucntions.

const mouseOver = () => {
  const [x, y] = arc.centroid(d)

  setSelected(true)
  tooltipContext.setTooltip({
    show: d.index !== null,
    x: x + offsetX + 30,
    y: y + offsetY + 30,
    content: d.data.stuffer,
    orientLeft: offsetX 

mouseOver is the active function. Mouse over an arc and it calculates its center, sets the arc to selected, and pushes necessary info into tooltip state. This triggers a re-render of the tooltip component and makes it show up.

Technically it triggers a re-render of our whole app because it's tied to App state. You could split that out in a bigger app. Or rely on React being smart enough to figure out the smallest possible re-render.

Deselecting the arc happens in a mouseOut function

const mouseOut = () => {
  setSelected(false)
  tooltipContext.setTooltip({ show: false })
}

Set selected to falls and hide the tooltip.

With all that defined, rendering our arc is a matter of returning a path with some attributes.

return (
  
)

Use the arc generator to create the shape, fill it with color, set up mouse events, add a dash of styling.

Render a donut

We did all the complicated state and tooltip stuff in individual arcs. The donut component uses a pie generator and renders them in a loop.

const StockingDonut = ({ data, x, y, r }) => {
  const pie = d3.pie().value(d => d.percentage)

  const color = chroma.brewer.set3
  return (
    
      {pie(data).map(d => (
        
      ))}
    
  )
}

d3.pie takes our data and returns all the info you need to build a donut. Start angles, end angles, stuff like that.

Render a grouping element that centers our donut on (x, y) coordiantes, render <Arc>s in a loop.

Make sure to pass offsetX and offsetY into each arc. Arcs are positioned relatively to our donut center, which means they don't know their absolute position to pass into the tooltip context. Offsets help with that.

And that's how you make tooltips in SVG with React hooks. Same concepts and complications apply if you're using normal React state or even Redux or something.

You need a global way to store info about the tooltip and some way to trigger it from sibling components.

PS: A neat way to useData

Yeah I've been doing that pattern a lot.

const [state, setState] = useState(null)
useEffect(() => doStuff().then(setState), [!state])

— Swizec Teller (@Swizec) December 8, 2018

Got tired of the useState/useEffect dance when loading data with hooks. Built a new hook called useData. That's a neat feature of hooks; you can make new ones.

function useData(loadData) {
  const [data, setData] = useState(null)

  useEffect(
    () => {
      loadData(setData)
    },
    [!data]
  )

  return data
}

Takes a loadData function, sets up useState for the data, uses an effect to load it, gives you setData so you can return the value, and returns the final value to your component.

You use it like this

function App() {
  const data = useData(setData =>
    d3
      .tsv("/data.tsv", d => ({
        stuffer: d.stuffer,
        percentage: Number(d.percentage)
      }))
      .then(setData)
  );

Much cleaner I think

Might be cleaner to take a promise and handle setData internally. Hmm ...

Thinking I might open source this, but it needs a few more iterations.

The post Tooltips … tooltips are not so easy 🧐 appeared first on A geek with a hat.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

This is a crosspost from the #ReactVizHoliday project where I livecode a new dataviz every day until Christmas. Thought you might be interested

Click this to get the daily email

Christmas movies at the box office

Christmas movies are the best movies. How much do they make at the box office? Show the power law distribution curve with a vertical barchart.

We built this one with React hooks because we can. Not a class-based component in sight

Styled components for styling, D3 for scales and data loading and parsing, hooks to hook it all together.

Loading data with React hooks

I looked around for a good data-loading hook. None could be found. So we made our own

Not that hard, as it turns out. You need a dash of useState to save the data you load, a bit of useEffect to run data loading on component mount aaaand… that’s it. Goes in your App function.

function App() {
  const [data, setData] = useState(null);

  useEffect(
    () => {
      d3.tsv("/data.tsv", d => {
        const year = Number(d.movie.match(/\((\d )\)/)[1]);
        return {
          movie: d.movie.replace(/\(\d \)/, ""),
          year: year,
          per_year: Number(d.box_office) / (2018 - year),
          box_office: Number(d.box_office)
        };
      }).then(setData);
    },
    [!data]
  );

The useState hook takes a default value and always returns current state – data – and a setter – setData.

useEffect runs our function on every component render. After committing to the DOM, I believe. We use d3.tsv to load and parse our Christmas movie dataset, use a parsing function to transform each row into an object with all the info we need, then call setData when he have it.

Each datapoint holds

  • a movie name
  • the year a movie was produced parsed from the movie name with a regex
  • the per_year revenue of the movie as a fraction
  • the total box_office revenue
Switch display modes with React hooks

Movie box office revenue follows a pretty clear power law distribution. The highest grossing movie or two make a lot more than the next best. Which makes way more than next one down the list, etc.

But how does age factor into this?

Home Alone has had 28 years to make its revenue. Daddy’s Home 2 is only a year old.

I decided to add a button to switch modes. From total box_office to per_year revenue. And boy, does it change the story. Altho maybe I’m being unfair because how long are theater runs anyway?

Driving that logic with React hooks looks like this

const [perYear, setPerYear] = useState(false)
const valueFunction = perYear ? d => d.per_year : d => d.box_office

// ...

 setPerYear(!perYear)}>
  {perYear ? "Show Total Box Office" : "Show Box Office Per Year"}

A useState hook gives us current state and a setter. We use the state, perYear, to define a value accessor function, and a butto’s onClick method to toggle the value.

We’re going to use that value accessor to render our graph. Makes the switch feel seamless.

Render

First, you need this bit in your App function. It renders in an SVG, if data exists.


  {data && (
    
  )}

That data && ... is a common trick. The return value of true && something is something, return value of false && something is nothing. Means when data is defined, we render; otherwise, we don’t.

Oh, and Svg is a styled SVG component. Gets a nice gif background when showKevin is set to true

The VerticalBarchart itself is a functional component. We said no classes right?

const VerticalBarchart = ({ data, width, height, value }) => {
  const yScale = d3
    .scaleBand()
    .paddingInner(0.1)
    .domain(data.map(d => d.movie))
    .range([0, height])
  const widthScale = d3
    .scaleLinear()
    .domain([0, d3.max(data, value)])
    .range([0, width])

  return (
    
      {data.map(d => (
        
          
          
            {d.movie}
          
        
      ))}
    
  )
}

We can define our D3 scales right in the render function. Means we re-define them from scratch on every render and sometimes, that’s okay. Particularly when data is small and calculating domains and ranges is easy.

Once we have a scaleBand for the vertical axis and a scaleLinear for widths, it’s a matter of iterating over our data and rendering styled and components.

Notice that we use the value accessor function every time we need the value of a datapoint. To find the max value for our domain and to grab each individual width.

Makes our chart automatically adapt to flicking that perYear toggle

That smooth width transition effect? That’s just CSS.

const Bar = styled.rect`
  fill: green;
  transition: width 500ms;
`

React hooks really do make life easy

What you learned today
  • the useState React hook
  • the useEffect React hook
  • that it’s okay to define D3 stuff in the render method; sometimes

Cheers,
~Swizec

The post Building a React dataviz with React hooks appeared first on A geek with a hat.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Yesterday, Corey asked me about the best advice I have to give.

It was for a podcast episode of The Write Stuff, his podcast about writing. Going to be a great episode. Gonna share when it’s out

But his question kept me up at night. It totally wasn’t battling Gatsby and mdx. Nu-uh, not me.

What is the best advice you can give to an aspiring writer, programmer, engineer, artist, entrepreneur, practitioner like you? A doer. Creator.

And I believe it’s this

  1. Your taste is better than your skill
  2. Practice every day
Your taste is better than your skill

Ira Glass speaks of The Gap. That place between taste and skill where growth happens.

You see, you get into this game because you have good taste. You read good writing, and you like it. You use delightful software, and you love it. You see a beautiful website, and you think it’s great. You see art, and you think, “Wow.”

That’s your taste. That’s what you’re trying to achieve.

And you write some stuff, build some code, make a website, draw some art. And you think it’s shit.

Your code doesn’t work, your writing doesn’t flow, your website looks like crap, and your art is kinda wonky.

That’s okay! That’s your taste leaping ahead of your skill.

To bridge the gap, you need a volume of work. And you need feedback. Feedback from yourself, from your peers, from everyone who will give it.

Volume of work is the only way. Make a thing. Fix the thing. Improve your skill until it matches your taste. Soon enough you’ll love the stuff you make.

And with practice your taste will improve as well. You might never catch up and that’s great!

Practice every day

Quickest way to get that volume of work? Practice!

If you practice once a year, I don’t think that counts. You’ll build some skill, but between sessions, you’ll forget everything you learn. Plus, in the 60 adult years you’ve got, you’d make only 60 attempts.

60 is not enough.

If you practice once a month, that’s better. 12 attempts per year, 180 in your adult life. Great for skills you aren’t that interested in or that take several days to complete. Like hiking. Or car repairs. Maybe launching tiny apps?

A few hours of practice every month could do wonders. Each session is intense, so you learn a lot. And a month is short enough that you remember what you’ve learned and can build on it.

But it’s often hard to clear an entire day for stuff.

That’s where weekly practice shines. 3120 attempts in your adult life.

Two orders of magnitude more practice than monthly practice. Calendars are weird like that.

Just 1 hour of practice per week. Like sitting down for an hour every Monday morning to write a newsletter to 10,000 people. Or going for a run. Or writing some code with that new stuff you’re learning.

That’s 3120 hours of practice in your adult life. A monthly practitioner would have to spend 5 hours each session to beat you. 5 hours is one hell of a commitment compared to just 1 hour

And what’s more: Weekly practice compounds more than monthly practice.

Because you remember more from last time, because you’re more in the groove, it’s easier to get started, easier to keep going, and you build skills on top of each other faster.

Which is where daily practice trumps all.

Even skipping weekends you get 15,120 hours of practice in your adult life. With just an hour a day.

That’s crazy town. What would take a lifetime with weekly practice takes just 12 years when you practice daily.

Yes, 12 years sounds like a lot. But compared to 60?

Practicing every day compounds like crazy as well. Each day is fresh in your mind when you start the next. You know what went wrong, you feel what went right. You know exactly what to do next.

You can even work on the same project over many days solving different parts of it because it’s fresh in your mind.

But how do you practice every day?

The best way is a job doing the thing you want to practice.

Jobs are great at feeding you realistic new challenges to solve every day. Jobs are great at forcing you to practice every day. Jobs are great at crushing your fear of the empty page and forcing you to sit down and get it done.

The hardest part of any creative endeavor is starting. That moment when you sit down and nothing happens for 5 minutes and you want to run away and hide in social media or Netflix or take a nap.

A job won’t let you do that. Gotta sit there and do the thing.

Once you’ve done the thing. A new challenge appears.

It’s great

When you can’t or don’t want to get a job doing the thing, your best bet is to challenge yourself. Or to find a challenge group.

The post Why you should write [code] every day appeared first on A geek with a hat.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Launching React for Data Visualization on turkey day was a last minute decision. So last minute, in fact, that I renamed the course Sunday evening. 2 days before the first email went out.

See, I’ve been working on this since early August.

Well, the first version of my React data visualization stuff went on sale in April 2015. Made $4,000 in its first month, too.

Fast forward to late 2018.

Book has seen 3 or 4 major rewrites. Kept up with React and D3. It’s been a great project, but sales are stagnating. A little tired of the topic. Looking for the next thing and not quite finding one.

Knowing I should update the book but ugh who cares…

Then the Director of Data Visualization at Visa emails me in July. I didn’t know such a thing as a director of data visualization existed, but he emailed me and said: "Dude, we love your React + D3v4 book. But come on. Can’t you update? It’s out of date."

When the director of data visualization at Visa emails you, that’s a strong signal. The market still cares. Real people with real needs. And money to spend.

Time for an update!

A flurry of activity. August was intense.

I put up a preorder on Gumroad. Working title "React + D3 2018". Asked my intern to build a landing page. Never used it. Wrote some quick copy for the Gumroad product description, set the price at $49, and emailed my list.

A few sales.

Okay, great. There’s interest.

This time, I wanted the book to have a real course. Not just some crappy little videos. A proper course that follows the book and teaches you everything.

I suck at recording courses.

Workshops! I’m good at holding workshops. We’re gonna have 3 live workshops. A bonus for everyone who preorders.

Bonus for me: Announcing each Sunday workshop is a great excuse to email my list.

And I can ask people what they learned, what excites them, and why they’re interested. Copywriting and sales gold right there.

Put up some ads. Got a cover design on 99designs. Kept emailing my list.

By the end of August, I had 104 preorders. A quarter updated book. Updated code samples. And a final cover design chosen via popular vote and Facebook ads.

The hard work begins

Preorders in hand and charged, a sinking feeling.

There’s no way I can finish this by the start of September. Not with a day job. Not when I’m spending every morning on mastermind and client calls.

So I emailed everyone.

Hey this is gonna take some time. Updating a 250-page book after JavaScript has been evolving for a year… it’s a lot of work. Editing those 4 hours of workshop video? Oof

Rolled up my sleeves and got to work. Every day, I would write a little. Code a little. Nudge things along.

Every few weeks, I’d send out an update. "Hey, here’s a new PDF. Hope you like. Still working on it. Thanks for your patience."

They understood. Everyone was great about it.

People kept buying the old book. My automations are left un-updated. My landing page is still for the old version. When you sign up for my mailing list, it still pitches you the old version of the book.

Ugh.

No matter! We’ll give people who ask a free upgrade to the new stuff. Everyone’s happy with this arrangement. Every now and then, I sweep the list, find everyone with a recent purchase, and offer them a coupon.

A few more React + D3 2018 buyers trickle in.

React for Data Visualization is born

At some point while updating the book, I realize this just isn’t going to work.

Too many quips about how silly it is that in 2018 I still can’t embed gifs in electronic books. Too many problems with emoji support for Markdown → PDF/epub/mobi generators. Too hard to make code nicely copy-pastable.

The UX is shit. I’m asking people to pay real money for this? We can do better!

React + D3 2018 was to become a full course. Hosted online. Embedded code editors, videos for everything. Make it real easy for people to learn.

Because let’s be honest, most people learning to code do so at a computer with a nice big monitor. They copy-paste code. They have a browser.

Why not put the book in the browser and make it easy for them?

Although I did see a lady watching coding videos on the elliptical the other day. That was cool. Not something she could do with an ebook.

Wow gal next to me is watching coding videos on the elliptical. So SF

— Swizec Teller (@Swizec) November 27, 2018

Hell, if you make your book an online course, you can neatly pair videos with each section.

Solves a major problem with video courses: They’re boring, and videos drag on forever. You wanna jump around. You wanna skip to the good parts. You wanna focus on what you’re trying to learn, not the author’s long-winded introduction.

With a video per section, subchapter even, I can ensure no single video is longer than 5 minutes. Keep them focused and on topic. Make it easy to jump around.

The teaching philosophy behind React for Data Visualization - YouTube

But editing 8 hours of video was kicking my ass. September came and went. October was behind us. End of November closing in fast.

Yes, 8 hours of video. Up from the earlier 4 hours. Had to record a workshop thing of building the main example in the course. That’s how you can have video with everything

A haphazard launch

So, moment of truth. Course not done. Markdown messing with us. Video editing kicking my ass. Launch window closing fast. 6 more weeks left in the year.

Gotta do something.

Will people pay money for this? Is it worth pressing on? Have I burnt out my audience? Does anyone still care? Can I afford to pay everyone who’s been helping me make it happen?

Shawn built a landing page. Then another. Then tweaked it. Then made it better. I still wasn’t ready with the copy so we could launch. Ugh.

Helder basically reinvented markdown pandoc something something. Trying to get emojis to work. No good. Then he invented a script and a hack that let us import markdown into Teachable. Awesome work, makes the whole thing viable.

And we can update the course with a git push

I been updating content and editing videos for months.

Was all the effort worth it? Am I throwing money down the drain paying people to help me make it happen?

Thanksgiving, Black Friday, and Cyber Monday are upon us. Now’s the time to find out!

Sunday night, I came up with a plan: Run a promotion for this unfinished course over turkey weekend . Set the price at a steep discount from the future full price.

$97 instead of $149.

Everyone can get the written parts immediately. And I promise to pump out the rest of video stuff as fast as I can. Additional chapters on WebGL, VR, React Native, and server-side-rendering coming by end of year.

To promote the sale, I would send an email to my entire list every day for the entire duration of the sale. Each email will have a little timer gif.

The experiment

The turkey launch experiment was geared to answer a few questions:

  1. Will people pay a solid price for this material?
  2. Will people hate me if I email every day?
  3. Can I generate some cash flow?
  4. Does this text with short videos format resonate with people?

Monday night, I renamed the course to React for Data Visualization. Much better title. Tuesday morning, I sent the first email:

Hey I’m launching this course. Short videos and text and code. I don’t have a landing page yet, you can buy it tomorrow. Reply if you care.

A few replies came in. That evening, we got Teachable markdown import to work. I hacked the cover from 99designs to fit the new title.

For the next few days, I kept emailing. Every morning, the first thing I did was write an email to my audience. Look at this wonderful thing. A beautiful promotion. You can learn and hack and have some fun and it’s cheaper than it’s ever gonna be.

I played with words. I played with angles. I explained it this way and that. I made an intro video. I sent emails long and short.

On Monday, I hosted a live Q&A with a little example coding. That went well. Should’ve done it sooner.

Then on Tuesday morning, I said "Last chance. Now or never.". Set the coupon to expire at 12:00 and went to work.

The result

7 emails
87 net subscribers lost
167 total unsubscribes
65 course sales
$6500 in revenue before Teachable fees

It’s not the 100 I was hoping for, but it’s cash in the bank, and I know people will pay a solid price for solid knowledge.

Time to roll up my sleeves and finish those videos.

Thank you everyone who purchased React for Data Visualization. You’re the best.

The post React for Dataviz Turkey 🦃 sale report for the curious appeared first on A geek with a hat.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Separate tags by commas
To access this feature, please upgrade your account.
Start your free month
Free Preview