Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
November 16, 2022 12:50 am GMT

React 102 - Basic data flows

So, youve learned the basics of React, then when you finally started using you started falling for one pitfall after another. Well, youre in the right place... finally!

Few things:

  • This is not to be a comprehensive guide
  • It is not for those just learning (hence 102 and not 101)
  • Also, I might take some generalizations for the sake of simplicity
    • as you may know JS is weird!
  • Finally, Ill pretend class components dont exist and so neither should you.
    • Im not saying you never should learn them, but today not at the 102 level.

If you do want to learn from zero, I recommend checking out the Beta React Docs, youll learn things there that many people using it for quite a while dont know (really!).

Finally, feel free to ask anything you might be having problems with. I love to help! (It might also help me find things to write, so win-win-win.)

Lifecycle and data flows

One of the biggest enemies of those starting React is to render stuff on the screen and have it change.

The basic render

function BasicRender(){  return (    <div>I'm rendering!</div>  )}

This will just render whatever is in the JSX.

With props

function WithProps({counter}){  return (    <div>The counter is: { counter }</div>  )}

First thing is that Im destructuring the props value.

This will render at { counter } whatever the counter value is. And every time it gets a new value, it will render the new value.

In this case, there are two things to know/remember:

  1. React shallowly compares to check if the component should rerender or not.
  2. The difference between primitives and non-primitives.

Knowing that you would know that primitives are checked by their value while everything else is checked by their reference.

With split flows

function WithIf() {  const random = Math.random();  if (random > 0.5) {    return (      <div>Random is big!</div>    );  } else {    return (      <div>Random is small!</div>    );  }}

if works just as you expect, there are rules about hooks where you cant initiate them inside split flows (you can only use them at the top level of the component), but after that, it works just like that.

With state

function WithState() {  const [counter, setCounter] = useState(0);  return (    <div>      <div>The counter value is: { counter }</div>      <button onClick={() => setCounter(counter + 1)}>Click me!</button>    </div>  )}function WithVariable() { // ???  let counter = 0;  return (    <div>      <div>The counter value is: { counter }</div>      <button onClick={() => counter += 1}>Click me!</button>    </div>  )}

First things first: the second approach, with variable, will not work.

Why? The short answer is that React needs a hook to know that something changed in order to rerender. This happens because functional components are closures and while it will indeed change the value internally, React will never know when to rerender the component and when it does rerender it will recreate it at the 0 value.

So, why does the first approach works? Thats the React magic, and for now, you should just accept it and what you can do with it.

Remember about the props? That also applies to the state.

Non-primitives like objects or arrays, if you simply change the value inside and return the object, this means that the reference doesnt change, even if you use the setState function.

The right way to think is that you always need to return a new reference, this means:

const newArr = [...oldArr];const newObj = {  ...oldObj};// spreading itself doesn't create new references// but the container you're spreading it into is a new reference.

With effects

function WithEffect1() {  const [state, setState] = useState('initial state');  useEffect(() => {    setState('useEffect []');    const timeout = setTimeout(() => {      setState('useEffect [] > timeout');    }, 500);    return () => {      setState('useEffect [] > return');      clearTimeout(timeout);    };  }, []);  return (    <div>{state}</div>  );}// you might see below duplicated, probably with the return,// but in dev mode, it's the Strict Mode// also, the initial state you'll never see in this situation// because react is really fast// If you slowed time down, you might see this happening:/** * initial state * useEffect [] * ~500 ms~ * useEffect [] > timeout */

This is the most basic use of useEffect. (Think of the setTimeout as a fetch call.)

Usually, we have more things going on, for example:

function WithEffect2() {  const [state, setState] = useState('initial state');  const [otherState, setOtherState] = useState(0);  useEffect(() => {    setState('useEffect [otherState]');    const timeout = setTimeout(() => {      setState('useEffect [otherState] > timeout');    }, 500);    return () => {      setState('useEffect [otherState] > return');      clearTimeout(timeout);    };  }, [otherState]);  return (    <div>      <div>{state}</div>      <button onClick={() => setOtherState(otherState + 1)}>        Change the Other State      </button>    </div>  );}/** * initial state * useEffect [otherState] * ~500 ms~ * useEffect [otherState] > timeout * ~click~ * // batched update, you'll never see the return * [useEffect [otherState] > return | useEffect [otherState]] * ~500 ms~ * useEffect [otherState] > timeout */

In this one there are more things happening.

But the idea is that the initial state will always be rendered first, regardless.

Then any and all useEffects will render the very next tick.

Anything thats asynchronous like promises or fetches calls will render so soon they finish.

Return statements inside the useEffect trigger before the next calculation when they are triggered.

Usually, you have a state that will be rendered first (usually youll render as a nullish value using a if to render a Loading... or something like this). Then the fetch will start, finish and set a new state, that will trigger a render, now with the values.

One thing to take care is that if you put in the dependency array the very state youre mutating in the useEffect, unless you have some check before, youll probably trigger an infinite recursion:

// this will break your page!useEffect(() => {    // add some exit condition if you really need to do this  setState(state + 1);}, [state])

Bonus: derived state

function DerivedState({ myValue }) {  const [state, setState] = useState(0);  const [secondState, setSecondState] = useState(0);  useEffect(() => {    setState(myValue * 2);  }, [myValue]);  useEffect(() => {    setSecondState(state * 2);  }, [state])  return (    <div>      <div>{state}</div>      <div>{secondState}</div>    </div>  );}function DoThisInstead({ myValue }) {  const stateToRender = myValue * 2;  const secondState = stateToRender * 2;  return (    <div>      <div>{stateToRender}</div>      <div>{secondState}</div>    </div>  );}

Things like this are common. If you already have the value, you dont need to put it inside a state and use useEffect to change it.

The way its been done in the first example youll see a cascade happening every time myValue changes and it might have unintended consequences.

If it comes from props, you can just calculate from it, when it changes, it will recalculate automatically.

If youre doing something like a filteredItems from some fetch, same thing. The useEffect will save to the state the values, then you just use a const filtered = state.filter().

Every time state changes, so will the filter. If you need, you can always memo the values.

Example deployed

GitHub of the Project

Again: questions? Just ask! (And to not be ignored read this helpful how to ask a good question)

And question for YOU! Where are you having trouble with React?

Cover Photo by Solen Feyissa on Unsplash


Original Link: https://dev.to/noriller/react-102-basic-data-flows-2lcp

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To