Boduch's Blog

Web Name: Boduch's Blog

WebSite: http://www.boduch.ca

ID:195357

Keywords:

Boduch,Blog,

Description:

Something that I struggled wrapping my head around when learning Next.js was refreshing data on a given page. It seemed that the only option was to trigger the browser to reload the page, which isn't at all what I was going for. Next.js does a fantastic job of populating the page with data, whether it's rendered on the server or in the browser. The initial page data and the plumbing needed to fetch it is something I just don't need to think about.In this post, I'll walk through my stumbling points for refreshing page data and the approach that I took to get around the issue. Let's start off by looking at a trivial page in a Next.js app:async function getInitialProps() { const response = await fetch('/api/some/endpoint'); const myFetchedData = await response.json(); return { myFetchedData };export default function MyPage(props) { return strong {props.myFetchedData} /strong MyPage.getInitialProps = getInitialProps;The getInitialProps() function fetches the data that our page needs. It makes a fetch() call to some imaginary API endpoint, gets the JSON response by calling response.json(), and returns an object with a myFetchedData property. The object that is returned is passed to the MyPage component as props. You can see that our component uses the props.myFetchedData value when rendering its content. Finally, the getInitialProps() function is assigned to MyPage.getInitialProps. This is how Next.js knows how to populate our page data. It will call this function on the server if this page is requested, or in the browser if this page is navigated to after the initial load.At this point, we have a page that loads data on initial render. But what happens if the data returned by the API endpoint has changed somehow? For example, some other component in our application changes this value or creates a new resource, etc. There are any number of reasons that you might want to refresh the data that's passed to MyPage as properties. This is where I got stuck.To illustrate the issue, let's add a refresh button to our page that simply calls the getInitialProps() function, thus calling the API again and getting updated data:async function getInitialProps() { const response = await fetch('/api/some/endpoint'); const myFetchedData = await response.json(); return { myFetchedData };export default function MyPage(props) { return ( main strong {props.myFetchedData} /strong button getInitialProps} Refresh /button /main MyPage.getInitialProps = getInitialProps;The onClick handler of the new refresh button gets us partway there. It makes the API call, which responds with updated data, but then it doesn't do anything with it. At this point, I realized that if your Next.js page has data that needs to be refreshed, it needs to be stateful. Thankfully, you can still use the getInitialProps mechanism of Next.js to handle data fetching for the initial page load. Here's what the pattern looks like:import { useState } from 'react';async function fetchData() { const response = await fetch('/api/some/endpoint'); const myFetchedData = await response.json(); return { myFetchedData };export default function MyPage(props) { const [ myFetchedData, setMyFetchedData ] = useState(props.myFetchedData); async function refresh() { const refreshedProps = await fetchData(); setMyFetchedData(refreshedProps.myFetchedData); return ( main strong {myFetchedData} /strong button Refresh /button /main MyPage.getInitialProps = fetchData;The MyPage component now has myFetchedData as state. The original myFetchedData property is still passed to the component because this is how the Next.js mechanism for data fetching passes data to our components. However, the useState() hook uses the myFetchedData property value as default state value for the myFetchedValue state. This means that you don't need to worry about the myFetchedValue property - just use the myFetchedValue state instead since it has the same value.Now that we've setup myFetchedValue to be stateful, we can implement a proper refresh function. But first, take note that we've renamed the getInitialProps() function to fetchData() because it's now more generic than fetching the initial page data. The refresh() function calls fetchData(), then calls setMyFetchedData() to update the state and re-render the page.To summarize how state can help you with refreshing data used by your Next.js pages, just remember:The default value passed to useState() is the value that is fetched initially by Next.js.You can reuse your data fetching function any time you need fresh data. Just update the state when you have the new data.If you're using Lodash, you have a handful of tools at your disposal for organizing logic into functions. This is a nice alternative to imperative if statements sprinkled throughout your code. For example, let's say that you only want to execute code if one of several choices is true. You could use the some() function as follows:const choices = [ { name: 'choice 1', value: true }, { name: 'choice 2', value: true }, { name: 'choice 3', value: false }if (_(choices).map('value').some()) { console.log('true');else { console.log('false');The choices array represents the choices that we have available. The name property isn't actually used for anything in this code. The value property is what we're interested in here. We want to run some code if any value is true.To do so, we're using an if statement. With the help of the map() and some() Lodash functions, we can easily check for this condition. In this case, the statement evaluates to true because there's two true values in the array. We can also check to make sure that every value is true before executing a piece of code:if (_(choices).map('value').every()) { console.log('true');else { console.log('false');In this case, the else path is followed because not every value is true.The _.some() and _.every() functions are helpful with simplifying the conditions evaluated by if statements. For example, _.some() provides the same result as chaining together a bunch of logical or (||) operators. Likewise, _.every() replaces logical and ( ) operators.The result is that instead of having to maintain the condition that's evaluated in the if statement, we can simply add new values to the choices collection. Essentially, this is a step toward declarative programming, away from imperative programming.Let's think about the if statement used above, and what it's actually doing. It's calling console.log() when some condition is true, and it's calling console.log() again when the condition is false. The problem with if statements like this is that they're not very portable. It'd be much easier to call a function with the possible choices as an argument, and the correct behavior is invoked.Here's what this might look like:const some = (yes, no) = (...values) = new Map([ [true, yes], [false, no] ]).get(_.some(values))();Let's break this code down:We've created a higher-order function called some() that returns a new function.The returned function accepts an arbitrary number of values. These are tested with Lodash's _.some().The some() function accepts yes() and no() functions to run based on the result of calling _.some(values)A Map is used in place of an if statement to call the appropriate logging function.With this utility, we can now compose our own functions that values as arguments, and based on those arguments, run the appropriate function. Let's compose a function using some():const hasSome = some( () = console.log('has some'), () = console.log('nope')Now we have a hasSome() function will log either "has some" or "nope", depending on what values are passed to it:hasSome(0, 0, 0, 1, 0);// - has somehasSome(0, 0, 0, 0);// - nopeNow any time that you want an if statement that evaluates simple boolean expressions and runs one piece of code or another, depending on the result, you can use some() to compose a new function. You then call this new function with the choices as the arguments.Let's create an every() function now that works the same way as some() except that it tests that every value is true:const every = (yes, no) = (...values) = new Map([ [true, yes], [false, no] ]).get(_.every(values))();The only difference between every() and some() is that we're using _.every() instead of _.some(). The approach is identical: supply yes() and no() functions to call depending on result of _.every().Now we can compose a hasEvery() function, just like we did with the hasSome() function:const hasEvery = every( () = console.log('has every'), () = console.log('nope')Once again, we've avoided imperative if statements in favor of functions. Now we can call hasEvery() from anywhere, and pass in some values to check:hasEvery(1, 1, 1, 1, 1)// - has everyhasEvery(1, 1, 1, 1, 0)// - nopeLodash has a _.cond() function that works similarly to our Map approach in some() and every(), only more powerful. Instead of mapping static values, such as true and false, to functions to run, it maps functions to functions. This allows you to compute values to test on-the-fly.Before we get too fancy, let's rewrite our some() and every() functions using _.cond():const some = (yes, no) = _.flow( _.rest(_.some, 0), _.cond([ [_.partial(_.eq, true), yes], [_.stubTrue, no]Let's break this down:The _.flow() function creates a new function by calling the first function, then passing it's return value to the next function, and so on.The _.rest() function creates a new function that passes argument values as an array to it's wrapped function. We're doing this with _.some() because it expects an array, but we just want to be able to pass it argument values instead.The _.cond() function takes an array of pairs. A pair is a condition function, and a function to call if the condition function returns true. The first pair that evaluates to true is run, and no other pairs are evaluated.The _.partial(_.eq, true) call makes a new function that tests the output of _.some().The _.stubTrue() function will always evaluate to true, unless something above it evaluates to true first. Think of this as the else in an if statement.We can use this new implementation of some() to compose the same hasSome() function that we created earlier and it will work the same way. Likewise, we can implement the every() function using the same approach.For something as simple as the some() and every() functions, the _.cond() approach doesn't present any clear advantage over the Map approach. This is because there are exactly two paths. Either the condition evaluates to true, or it doesn't. Often, we're not working with simple yes/no logical conditions. Rather, there are a number of potential paths.Think of this as a an if-else statement with lots of conditions. Suppose we had the following conditions:const condition1 = false;const condition2 = true;const condition3 = false;Instead of a simple yes/no question with two potential paths, now we have 3. Later on, we might have 4, and so on. This is how software grows to be complex. Here's how we would evaluate these conditions and execute corresponding code using _.cond():const doStuff = _.cond([ [_.constant(condition1), () = console.log('Condition 1')], [_.constant(condition2), () = console.log('Condition 2')], [_.constant(condition3), () = console.log('Condition 3')]doStuff();// - Condition 2For each of the condition constants that we defined above, we're using the _.constant() function in _.cond(). This creates a function that just returns the argument that is passed to it. As you can see, console.log('Condition 2') is called because the function returned by_.constant(condition2) returns true.It's easy to add new pairs to _.cond() as the need arises. You can have 20 different execution paths, and it's just as easy to maintain as 2 paths.In this example, we're using static values as our conditions. This means that doStuff() will always follow the same path, which kind of defeats the purpose of this type of code. Instead, we want the path chosen by _.cond() to reflect the current state of the app:const app = { condition1: false, condition2: false, condition3: trueInstead of using _.const(), we'll have to somehow pass the app into each evaluator function in _.cond():const doStuff = _.cond([ [_.property('condition1'), () = console.log('Condition 1')], [_.property('condition2'), () = console.log('Condition 2')], [_.property('condition3'), () = console.log('Condition 3')]The _.property() function creates a new function that returns the given property value of an argument. This is where the _.cond() approach really shines: we can pass arguments to the function that it creates. Here, we want to pass it the appobject so that we can process its state:doStuff(app);// - Condition 3app.condition1 = true;app.condition3 = false;doStuff(app);// - Condition 1When doStuff() is called the first time, theconsole.log('Condition 3') path is executed. Then, we change the state of app so that condition1 is true and condition3 is false. When doStuff() is called again with app as the argument, theconsole.log('Condition 1') path is executed.So far, we've been composing functions that use console.log() to print values. If you write smaller functions that return values instead of simply printing them, you can combine them to build more complex logic. Think of this as an alternative to implementing nested if statements.As an example, suppose we have the following two functions:const cond1 = _.cond([ [_.partial(_.eq, 1), _.constant('got 1')], [_.partial(_.eq, 2), _.constant('got 2')]const cond2 = _.cond([ [_.partial(_.eq, 'one'), _.constant('got one')], [_.partial(_.eq, 'two'), _.constant('got two')]These functions themselves follow the same implementation approach, using _.cond(). For example, cond1() will return the string 'got 1' or 'got 2', depending on the number supplied as an argument. Likewise, cond2() will return 'got one' or 'got two', depending on the string argument value.While we can use both of these functions on their own, we can also use them to compose another function. For example, we could write an if statement that would determine which one of these functions to call:if (_.isFinite(val)) { cond1(val);} else if (_.isString(val)) { cond2(val);Remember, this approach isn't very portable. To make it portable, in the sense that we don't have to write the same if statement all over the place, we could wrap the whole thing in a function. Or, we could just use _.cond() to compose it:const cond3 = _.cond([ [_.isFinite, cond1], [_.isString, cond2]cond3(1);// - "got 1"cond3(2);// - "got 2"cond3('one');// - "got one"cond3('two');// - "got two"Using _.cond(), you can compose complex logic by reusing existing functions. This means that you can keep using these smaller functions where they're needed, and you can use them as pieces of larger functions.I m pleased to announce the availability of Learning jQuery 3. This is the fifth edition of the book, and given its track record, I didn t want to diverge from what has worked so well over all these years.Something that I struggled wrapping my head around when learning Next.js was refreshing data on a given page. It seemed that the only option...Google has officially changed its name to Topeka . I found this link by Topekaing April Fools .The jQuery UI toolkit provides us with a tabs widget to logically group elements on our page. This is useful as a top-level navigational ...The built-in setTimeout() JavaScript function defers the the execution of a given function till a given number of milliseconds have passed....The disabled option of the accordion widget let s you disable the entire widget when set to true. But I don t want to disable the ...I always end up implementing some convoluted JavaScript inside a Handlebars helper whenever I have to render lists with any depth. Think tr...

TAGS:Boduch Blog 

<<< Thank you for your visit >>>

Websites to related :
BABCPNW | Seattle | British-Amer

  News PolicyNews PolicyNews Policy Log InBABC Impact: Managing Risk Around the Shift to Hybrid WorkingBABC member Osborne Clarke explores the futur

RugbyUtah.com is for sale | Huge

  Buy now for: $3,295Questions? Talk to a domain expert: 1-303-893-0552Make 12 monthly payments of $274.58 | Pay 0% interest | Start using the domain to

Keithley Funeral Homes | Hays KS

  Services Unique As Life Everyone's Life is Unique, Worth Celebrating and Honoring. OUR SERVICES Live Well, Plan Ahead Planning Ahead Can Make All the

Brock Pest Control

  Satisfaction Guarantee We back our work, and strive for more than just customer satisfaction. BROCK LAWN PEST CONTROLProviding professional, eco-frie

Webster, TX Full Service Auto Re

  “Quality service and reasonable prices. I especially like to make appointments. When I arrive for my appointment, they start work right away.” Bro

THE PLANT AT KYLE

  A one-of-a-kind architectural landmark within easy driving distance of Austin and San Antonio.Plan A VacationEnjoy vacation time relaxing with friends

Kyle Landry | Classically Train

  Hello everyone! A new website is being made to replace this one, specifically to address some of the design issues on this site including the most rec

Orthodontist in Sudbury, ON | Dr

  Welcome Dr. Paul KyleOrthodontist in Sudbury, ONDr. Paul Kyle has been practicing orthodontics in Sudbury, Ontario since 1991. He obtained his orthodo

amberwoodhomeowners.org | your s

  Click the link create a login ID and Password, here you will find information on your residents. Continue Reading... Halloween Decorating Contest Oct

Atkins For Seizures - Atkins for

  Atkins For SeizuresMichael Koski2021-04-26T22:18:10+00:00 The Johns Hopkins Pediatric Neurology Center has long supported seizure management through D

ads

Hot Websites