Asked  7 Months ago    Answers:  5   Viewed   15 times

I have been learning React over the past few days, looking at a few tutorials and explanations regarding the different ways in which you can write different elements. However there is one I have been most curious about - the setState function to update/override the state properties of a component.

For example, imagine I have a class with the following:

class Photos extends React.Component {
    constructor() {
        super()
        state = {
            pictures: []
        }
    }

   componentDidMount() {
      // This is where the fetch and setState will occur (see below)
   }

    render() {
       return {
          <div className="container">
             {this.state.pictures}
          </div>
       }
    }
}

This example sees me fetch images from an API.

Given that I have performed my fetch, map and return for this function - I will then update the pictures: [] state array with the results gained in the API call.

My question stems from the differing methods I have seen regarding how to update/override the pictures state property.

I have seen it written in 2 varying ways:

1) This seems to be a very simple and easy to read method

this.setState({pictures: pics})

2) This is more complex but I have see it described as a more safe method

this.setState(prevState => ({
   pictures: prevState.pictures.concat(pics)
}))

Could somebody please explain the merits of using either? I want to be consistent with code in the future, dealing with props and states etc, so the most versatile method would of course be preferred.

 Answers

29

First things first, in your case the two syntaxes are entirely different, what you might be looking for is the difference between

this.setState({pictures: this.state.picture.concat(pics)})

and

this.setState(prevState => ({
   pictures: prevState.pictures.concat(pics)
}))

To understand why the second method is a preferred one,you need to understand what React does with setState() internally.

React will first merge the object you passed to setState() into the current state. Then it will start that reconciliation thing. Because of the calling setState() might not immediately update your state.

React may batch multiple setState() calls into a single update for better performance.

Consider the simple case, to understand this, in your function you might call setState() more than once like:

myFunction = () => {

   ...
   this.setState({pictures: this.state.picture.concat(pics1)})
   this.setState({pictures: this.state.picture.concat(pics1)})
   this.setState({pictures: this.state.picture.concat(pics1)})

   ...
}

which isn't a valid use case in a simple app but as the app gets complex, multiple setState() calls may be happening from multiple places, doing the same thing.

So now to perform an efficient update, React does the batching thing by extracting all the objects passed to each setState() call, merges them together to form a single object, then uses that single object to do setState(). According to the setState() documentation:

This form of setState() is also asynchronous, and multiple calls during the same cycle may be batched together. For example, if you attempt to increment an item quantity more than once in the same cycle, that will result in the equivalent of:

Object.assign(
  previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)

Subsequent calls will override values from previous calls in the same cycle, so the quantity will only be incremented once. If the next state depends on the current state, we recommend using the updater function form, instead:

this.setState((state) => {
  return {quantity: state.quantity + 1};
});

For more detail, see:

  • State and Lifecycle guide
  • In depth: When and why are setState() calls batched?
  • In depth: Why isn’t this.state updated immediately?

setState() - Other APIs - React.Component – React.

So if any of the objects contains the same key, the value of the key of the last object with same key is stored. And hence the update only happens once with the last value.

Demo Codesandbox

Tuesday, June 1, 2021
 
francadaval
answered 7 Months ago
48

UPDATE -- use this instead:

<script type="text/babel" src="./lander.js"></script>

Add type="text/jsx" as an attribute of the script tag used to include the JavaScript file that must be transformed by JSX Transformer, like that:

<script type="text/jsx" src="./lander.js"></script>

Then you can use MAMP or some other service to host the page on localhost so that all of the inclusions work, as discussed here.

Thanks for all the help everyone!

Thursday, June 3, 2021
 
fhonics
answered 6 Months ago
43

This is what I put together for myself after reading the spec:

  • Any method which takes a single parameter can be used as an infix operator: a.m(b) can be written a m b.
  • Any method which does not require a parameter can be used as a postfix operator: a.m can be written a m.

For instance a.##(b) can be written a ## b and a.! can be written a!

  • Postfix operators have lower precedence than infix operators, so foo bar baz means foo.bar(baz) while foo bar baz bam means (foo.bar(baz)).bam and foo bar baz bam bim means (foo.bar(baz)).bam(bim).
  • Also given a parameterless method m of object a, a.m.m is valid but a m m is not as it would parse as exp1 op exp2.

Because there is a version of mkString that takes a single parameter it will be seen as an infix opertor in fromFile(file) mkString map caesar(k)_. There is also a version of mkString that takes no parameter which can be used a as postfix operator:

scala> List(1,2) mkString
res1: String = 12

scala> List(1,2) mkString "a"
res2: String = 1a2

Sometime by adding dot in the right location, you can get the precedence you need, e.g. fromFile(file).mkString map { }

And all that precedence thing happens before typing and other phases, so even though list mkString map function makes no sense as list.mkString(map).function, this is how it will be parsed.

Thursday, June 10, 2021
 
Null
answered 6 Months ago
99

Looks like Chris's comment is the correct answer:

If title is a string, use toString() before toLowerCase():

return episode.title.toString().toLowerCase().indexOf(props.filterText.toString().toLowerCase()) > -1;
Sunday, September 26, 2021
 
Ergwun
answered 2 Months ago
45

On interfaces. First of all, I think you can see interfaces as just named pairs of functions. If you have:

type ICacheProvider =
  abstract Get : string -> option<obj>
  abstract Set : string * obj -> unit

then this is pretty much equivalent to having a pair (or a record) of functions:

type CacheProvider = (string -> option<obj>) * (string * obj -> unit)

The benefit of using interfaces is that you give the type a name (you would get that with records too) and you are more clearly expressing your intention (other components can implement the interface).

I think using an interface is a good idea if you have more than 2 functions that are often passed to some other function together - this way, you avoid having too many parameters.

Module or class. The real difference in your code is whether to use module with higher-order functions or a class that takes the interface as constructor argument. F# is a multi-paradigm language that combines functional and OO style, so I think using classes in this way is perfectly fine. (You can still benefit from the functional style when defining data types to represent the domain etc.)

One thing to keep in mind is that functional programming is all about composition. This might not be as useful in this case, but I always prefer writing code that I can compose to add more functionality rather than code that requires me to provide something when I want to use it.

Perhaps you could write it so that your database access code does not do caching directly (this would include all the database queries and pre-processing logic):

module UserService =
    let getAll () = (...)
    let tryGetByID id = (...)
    let add name = (...)

...and then define a type that wraps this and adds caching (and this would then be used by the main type of the web application - it is quite similar to the type you defined in your example, but now we are separating the database access and memorization using a cache provider):

type UserService(cacheProvider:ICacheProvider) =
    member x.GetAll() = cacheProvider.memoize UserSerivce.getAll ()
    member x.TryGetByID id = cacheProvider.memoize UserService.tryGetByID id
    member x.Add name = cacheProvider.memoize UserService.add name

Summary. But - I think your approach using a class that takes ICacheProvider is perfectly fine - F# is pretty good in mixing functional and object oriented style. The example I posted is really just a possible extension that might be useful in bigger projects (if you wanted to use functional aspects and clearly separate different aspects of the functionality)

Saturday, October 30, 2021
 
Dale Zak
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share