<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bryan Smith</title>
    <description>The latest articles on DEV Community by Bryan Smith (@orionseven).</description>
    <link>https://dev.to/orionseven</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1007306%2Fe7b65c2e-b011-40b3-b23e-685dda6e3fd6.png</url>
      <title>DEV Community: Bryan Smith</title>
      <link>https://dev.to/orionseven</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/orionseven"/>
    <language>en</language>
    <item>
      <title>Submitting a form in Nextjs 14 with onBlur, server actions, and validation</title>
      <dc:creator>Bryan Smith</dc:creator>
      <pubDate>Wed, 07 Feb 2024 19:10:21 +0000</pubDate>
      <link>https://dev.to/orionseven/submitting-a-form-in-nextjs-14-with-onblur-server-actions-and-validation-55dl</link>
      <guid>https://dev.to/orionseven/submitting-a-form-in-nextjs-14-with-onblur-server-actions-and-validation-55dl</guid>
      <description>&lt;p&gt;Want a straightforward article on submitting a form in Nextjs to a server action using an HTML event &lt;code&gt;onBlur&lt;/code&gt;? And with form validation?&lt;/p&gt;

&lt;p&gt;Then this is the article for you. I found the documentation on Nextjs.org lacking, so I'm putting this here for you.&lt;/p&gt;

&lt;p&gt;This article is suitable for other events, such as &lt;code&gt;onChange&lt;/code&gt;. Still, I prefer &lt;code&gt;onBlur&lt;/code&gt;; as you know, the user is done typing, and you don't have to mess with debouncing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Form
&lt;/h2&gt;

&lt;p&gt;This tutorial has a simple &lt;code&gt;input&lt;/code&gt; field within the &lt;code&gt;form&lt;/code&gt; asking for a name. &lt;/p&gt;

&lt;p&gt;I want the user to save the field without hitting a submit button. So, the form has no buttons; the user types and the code does the rest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";

import React from "react";
import { useFormState } from "react-dom";
import { saveName } from "@/src/libs/actions/SaveName";

export default function NameForm() {

  const initialState = { message: "", errors: {} };
  const [state, formAction] = useFormState(saveName, initialState);

  return (
    &amp;lt;form action={formAction}&amp;gt;
      {state &amp;amp;&amp;amp; state.message &amp;amp;&amp;amp; &amp;lt;div&amp;gt;{state.message}&amp;lt;/div&amp;gt;}
      &amp;lt;div&amp;gt;
        &amp;lt;label htmlFor="name"&amp;gt;
          Enter your name
        &amp;lt;/label&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;input
              name="name" 
              id="name"   
              type="text"
              required
              placeholder="Ex. Brandon Sanderson"
              onBlur={async (e) =&amp;gt; { 
                const formData = new FormData();
                formData.append("name", e.target.value);
                await formAction(formData); 
              }}
          /&amp;gt;
          {state &amp;amp;&amp;amp; state.errors?.name &amp;amp;&amp;amp;
            name((error: string) =&amp;gt; (
              &amp;lt;p key={error}&amp;gt;
                {error}
              &amp;lt;/p&amp;gt;
            ))}
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/form&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, let's break this down. To make our lives easier and provide error messages, we're using the &lt;a href="https://react.dev/reference/react-dom/hooks/useFormState"&gt;React hook&lt;/a&gt; &lt;code&gt;useFormState&lt;/code&gt;. As of this writing, this newer feature is only available in a Canary build. But it's been stable for me.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const initialState = { message: "", errors: {} };
const [state, formAction] = useFormState(saveName, initialState);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We first create a state object, which we'll use to handle our validation messages.&lt;/p&gt;

&lt;p&gt;We then create our state object and formAction by passing to useFormState a reference to our function (a &lt;a href="https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations"&gt;NextJS server action&lt;/a&gt;) and the initial state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form action={formAction}&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the Nextjs syntax to call our server action in our form. If you don't include this when a user hits enter within a field, it will not call your server action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input
  name="name" 
  id="name"   
  type="text"
  required
  placeholder="Ex. Brandon Sanderson"
  onBlur={async (e) =&amp;gt; { 
    const formData = new FormData();
    formData.append("name", e.target.value);
    await formAction(formData); 
  }}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;input&lt;/code&gt; is standard right up to the &lt;code&gt;onBlur&lt;/code&gt;. We cannot call the form's &lt;code&gt;action&lt;/code&gt; from &lt;code&gt;onBlur&lt;/code&gt;, but we can trigger the same action ourselves. But you must make a &lt;code&gt;FormData&lt;/code&gt; object yourself. &lt;code&gt;FormData&lt;/code&gt; is just a simple interface; you can create it with a call to &lt;code&gt;new FormData()&lt;/code&gt;. We then populate it with an &lt;code&gt;append&lt;/code&gt;, which requires a key/value pair. Then, we call our server action created via the hook.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Server Action
&lt;/h2&gt;

&lt;p&gt;For the server action, we'll include validation since we want to give the user an experience that lets them know if something has gone wrong.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use server";

import { z } from 'zod';

const NameFormSchema = z.object({
    name: z.string().min(2, { message: 'You must enter at least two characters'}),
});

export type State = {
    errors?: {
        name?: string[];
    };
    message?: string | null;
}

export async function saveWebsite (prevState: State | undefined, formData: FormData) {

    // Validate Inputs
    const validatedFields = WebsiteFormSchema.safeParse({
        name: formData.get('name'),
    });

    // Handle Validation Errors
    if (!validatedFields.success) {
        const state: State = {
            errors: validatedFields.error.flatten().fieldErrors,
            message: 'Oops, I think there\'s a mistake with your inputs.',
        }

        return state;
    }

    // Do your save here...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm a big fan of &lt;a href="https://zod.dev"&gt;Zod&lt;/a&gt;, a typescript validation library. You could roll this yourself. But why do life in hard mode?&lt;/p&gt;

&lt;p&gt;Let's break this code down&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const NameFormSchema = z.object({
    name: z.string().min(2, { message: 'You must enter at least two characters'}),
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zod uses a schema format. So first, you need to tell it the schema of the form you'll be using. Each field you're submitting will have an entry. In this case, just one field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export type State = {
    errors?: {
        name?: string[];
    };
    message?: string | null;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the type we'll be using for our form state. We must define its type fully because we're populating it and using TypeScript. We use this object to pass back errors. If you look at the form, you can see how it's used, as it's fairly straightforward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Validate Inputs
    const validatedFields = WebsiteFormSchema.safeParse({
        name: formData.get('name'),
    });

    // Handle Validation Errors
    if (!validatedFields.success) {
        const state: State = {
            errors: validatedFields.error.flatten().fieldErrors,
            message: 'Oops, I think there\'s a mistake with your inputs.',
        }

        return state;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validation is also straightforward with Zod. First, we safely parse the fields we want. Then, we check for any errors that were created during the parse. If they exist, we package them up into our state object.&lt;/p&gt;

&lt;p&gt;The last step is up to you. Save to a database or call an API. But be sure to capture any errors and use the state object if they occur.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>frontend</category>
      <category>typescript</category>
    </item>
    <item>
      <title>How to find problems worth solving</title>
      <dc:creator>Bryan Smith</dc:creator>
      <pubDate>Thu, 30 Nov 2023 15:30:00 +0000</pubDate>
      <link>https://dev.to/orionseven/how-to-find-problems-worth-solving-53og</link>
      <guid>https://dev.to/orionseven/how-to-find-problems-worth-solving-53og</guid>
      <description>&lt;h2&gt;
  
  
  It's a surprisingly common question
&lt;/h2&gt;

&lt;p&gt;My answer to everyone is, in short, “Always be looking for pain.” Then be sure to evaluate and validate it (&lt;a href="https://www.productfoundry.co/issues/issue-9-buiders-are-gonna-build"&gt;or at least start evaluation and validation as you code. I get it, builders are gonna build&lt;/a&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Constantly be listening for pain
&lt;/h2&gt;

&lt;p&gt;You can find ideas in surprising places. Some of my favorites are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At the office (assuming you have a 9-5)&lt;/li&gt;
&lt;li&gt;Friends &amp;amp; family in a professional field&lt;/li&gt;
&lt;li&gt;Online communities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I almost always look for B2B problems when looking for pains to solve. I’m not interested in B2C (other than my desire to make a game someday).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As long as you’re listening and reading with intention, you’ll find problems to solve.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s as simple as that. If you have a problem-solving mindset, you’ll start running across problems constantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning —&lt;/strong&gt; Just because you can solve a problem doesn’t always mean your audience wants or needs you to. My wife can attest to this 😂 but also that I’ve finally learned it.&lt;/p&gt;

&lt;p&gt;So what are people saying?&lt;/p&gt;

&lt;h2&gt;
  
  
  At the office…
&lt;/h2&gt;

&lt;p&gt;One of my Boring Business SaaS experiments was born out of a common problem with many solutions on the market today. But many have too much overhead or have moved upmarket to enterprise buyers, thus making room for me.&lt;/p&gt;

&lt;p&gt;Like most ideas at the office, it started with a conversation like this, “Hey, do we have something to let me do X?”&lt;/p&gt;

&lt;p&gt;But many other ideas are from being stuck doing a task manually or using an existing software solution that sucks (I’m pretty sure Notion started because one of their founders had to use Confluence).&lt;/p&gt;

&lt;p&gt;…many ideas are from being stuck doing a task manually or using an existing software solution that sucks&lt;/p&gt;

&lt;h2&gt;
  
  
  To Friends &amp;amp; family…
&lt;/h2&gt;

&lt;p&gt;People complain they don’t know an industry well enough to find ideas. That’s fine, but guess who does? Someone in that field.&lt;/p&gt;

&lt;p&gt;I know people in construction, legal, medical, and housekeeping in my family. A couple are even business owners. In a simple conversation on how things are going, invariably, something will scream, “There’s a pain in my field.”&lt;/p&gt;

&lt;p&gt;Case in point&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My sister-in-law has a housekeeping business. Running a cleaning business has unique scheduling problems, including billing, etc.&lt;/li&gt;
&lt;li&gt;My brother-in-law is a lawyer. Wow, talk about an industry behind the curve in technology! Back office problems, legal solution problems, and so on, there’s plenty of room to build something.&lt;/li&gt;
&lt;li&gt;My wife and another brother-in-law are in the medical field. This one is harder to get into solution-wise because of HIPAA requirements. But it’s a field dominated by one large vendor. There are all sorts of interesting problems at play. And it has a rich history of doctors championing new solutions.&lt;/li&gt;
&lt;li&gt;Another brother-in-law (Can you tell my wife is from a large family?) was in construction. Again, there are tons of things that could be improved. Tracking building supplies, scheduling subcontractors, invoicing, and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I could go on. I have friends in state government, friends working for other software companies, doing all sorts of things. But instead, let me give you an example.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Meet Wouter&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Wouter, whom I met in the MicroConf Connect community back in 2020 has a SaaS for law firms. It simplifies intake for new clients.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;He’s not a lawyer. His wife is.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And that’s where the story of his SaaS started. His wife’s own frustration with the software systems and the duplicative work they often required.&lt;/p&gt;

&lt;p&gt;There had to be a better way.&lt;/p&gt;

&lt;p&gt;And there was. Wouter started in 2020 and 3 years later is rapidly growing and solving this exact problem.&lt;/p&gt;

&lt;p&gt;All because he was listening for pain.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Online Communities…
&lt;/h2&gt;

&lt;p&gt;People talk.&lt;/p&gt;

&lt;p&gt;They also love to complain.&lt;/p&gt;

&lt;p&gt;Somedays, that’s what I think the internet was made for.&lt;/p&gt;

&lt;p&gt;But this is good news for you. Because if you’re looking for a problem to solve, a problem people are discussing online is a great place to start.&lt;/p&gt;

&lt;p&gt;Amy Hoy is the champion of finding problems this way. She even coined a phrase called &lt;a href="https://stackingthebricks.com/podcast/ep42-what-is-sales-safari-with-eteinne-garbugli/"&gt;Sales Safari&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A quick recap is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;People gather online&lt;/li&gt;
&lt;li&gt;Find an audience you think you can speak to or help&lt;/li&gt;
&lt;li&gt;Read what they’re talking about&lt;/li&gt;
&lt;li&gt;Sort what you’re hearing&lt;/li&gt;
&lt;li&gt;You’ll see common patterns&lt;/li&gt;
&lt;li&gt;There are solutions you can provide (not just software!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you get in the mindset, you can’t help but find problems. But is that idea worth solving?&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem Validation: Is software the solution?
&lt;/h2&gt;

&lt;p&gt;As the saying goes, everything looks like a nail when your only tool is a hammer.&lt;/p&gt;

&lt;p&gt;A lot of indie hackers need help at this stage. They have an idea and know how to write software, so they go write software.&lt;/p&gt;

&lt;p&gt;But the real question is, is software the right solution?&lt;/p&gt;

&lt;p&gt;This is where I lean in and ask more questions, forcing myself to consider the alternatives.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Could a simple update to training be a better solution?&lt;/li&gt;
&lt;li&gt;Could a change in the process fix it?&lt;/li&gt;
&lt;li&gt;Does it even need to be done?! (Lots of things get done in corporate America that waste time and resources.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Amongst other things, I’m checking my bias toward software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Market research — Are there already competitors (I hope so!)
&lt;/h2&gt;

&lt;p&gt;Next, I want to see if there’s already competition. And I’m hoping there is. I’ve already done the “create a new market game” (future article), which is exhausting.&lt;/p&gt;

&lt;p&gt;I like boring SaaS problems. Problems that have been solved by others because they’ve done one of the most challenging lifts for me already. &lt;strong&gt;They’ve validated there’s a market already.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many IndieHackers are afraid of competition. But guess what!? There are 8 billion people on earth. And there are over 334 million companies worldwide. &lt;strong&gt;There’s plenty of room for you.&lt;/strong&gt; This is why having a mindset of abundance is so essential.&lt;/p&gt;

&lt;p&gt;As I look into competitors, I’m doing the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I’m evaluating if there is a broader need. Current solutions == yes, no solutions be wary.&lt;/li&gt;
&lt;li&gt;I’m looking for clues on how big of a market there is (a bunch of people fighting in the same small pond is not a good idea).&lt;/li&gt;
&lt;li&gt;I’m seeing what people are saying about these companies. Competition is a fantastic source of intel. Looking at what people complain about in their current software is a great way to find your space.&lt;/li&gt;
&lt;li&gt;I’m looking to see if others are seeking the same pain. And I’m looking to see if their language is the same as the competitors I’ve identified.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro tip -&lt;/strong&gt; I like looking at companies’ language in their hero statement and searching in communities to see if others are asking for solutions that match that language. The more asking, the better.&lt;/p&gt;

&lt;p&gt;I like spaces where competition is already happening, AND people are unsure of a solution (thus complaining about the pain).&lt;/p&gt;

&lt;p&gt;Also, it’s a great way to start thinking about your positioning.&lt;/p&gt;

&lt;p&gt;This is great because next up, I’m thinking about how to reach my audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Marketing — How will I reach my audience?
&lt;/h2&gt;

&lt;p&gt;As I dig in, look at the competition, and reflect on how I discovered this pain, I ask one key question: “Why were they not already aware of a solution?”&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maybe they are, but what they see in the market doesn’t match their need?&lt;/li&gt;
&lt;li&gt;Perhaps they are priced out?&lt;/li&gt;
&lt;li&gt;Maybe they’re not the actual purchaser? In other words, there may be demand, but no one is willing to pay?&lt;/li&gt;
&lt;li&gt;Perhaps they weren’t aware of a solution, so there’s a market mismatch in positioning and pain? (See the pro tip above)&lt;/li&gt;
&lt;li&gt;The list goes on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m asking myself these questions because before committing to an idea, I always want to know what my marketing plan will look like.&lt;/p&gt;

&lt;h2&gt;
  
  
  That’s great, but how do I select one idea over another?
&lt;/h2&gt;

&lt;p&gt;I’m probably thinking about 5 to 10 ideas at any given time. That’s why I could rapidly come up with my two choices for my Boring SaaS Business Experiment.&lt;/p&gt;

&lt;p&gt;At this point, for all the ideas I’m deciding from, I’ve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decided there is a pain worth solving for because...&lt;/li&gt;
&lt;li&gt;There’s competition.&lt;/li&gt;
&lt;li&gt;The market is big enough for me, too.&lt;/li&gt;
&lt;li&gt;I have an idea of how I can reach them.&lt;/li&gt;
&lt;li&gt;And I care about the problem enough to grind it out. Building a SaaS is challenging on the best of days. If you’re not passionate about the problem, walk away.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last one is a big one. There are plenty of ideas. Find the ones you’re interested in.&lt;/p&gt;

&lt;p&gt;So there you have it. That’s how I find ideas and evaluate them. Ultimately, I’m looking for ones where the market is big enough, and it fits my interests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is there another way to evaluate ideas?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/asmartbear"&gt;Jason Cohen&lt;/a&gt; of WPEngine and SmartBear thinks so. He’s has a great post, “&lt;a href="https://longform.asmartbear.com/problem/"&gt;Excuse me, is there a problem?&lt;/a&gt;”&lt;/p&gt;

&lt;p&gt;He’s noticed something I’ve seen as well. Many people are trying to solve problems that are not viable to build a business. Not even a solopreneur company.&lt;/p&gt;

&lt;p&gt;Check out the article. It’s written with indie hackers in mind. &lt;strong&gt;He provides an excellent evaluation flow diagram and an interesting rubric to evaluate ideas.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>startup</category>
    </item>
  </channel>
</rss>
