Oral  Brekke

Oral Brekke

1651780560

Hook into Beam Events and Emit Them to NodeCG Compatible Channels

nodecg-beam

Hook into Beam Events and emit them to NodeCG Compatible channels.

This is a NodeCG 0.8 bundle.

TODO

Events

  • Follow
  • Sub -Thanks so much NonGenericName
  • Misc Update Events
  • Hosting
  • Status
  • Polls

API Loading

For events that happen whilst the bundle is offline the Beam API can be pulled and compared to the internal data store.

Installation

  1. Install to nodecg/bundles/nodecg-beam
  2. Create a config file in nodecg/cfg/ called nodecg-beam.json
  3. Add the channels you wish to track with e.g.
{
  "channels":["ChannelName"]
}

Use

This module does nothing by itself. To use it for alerts and other media you need to listen to the events it outputs.

Checkout prime-alerts for a basic example

Follow

nodecg.listenFor('follow', 'nodecg-beam', function(data) {
  //Snazzy Alerts
}
{
  "name":"username",
  "channel":"channel",
  "ts":3123123123123
}

Update

nodecg.listenFor('update', 'nodecg-beam', function(data) {
  //Snazzy Alerts
}

This event is mostly parroted from the underlying Beam layer. Use them if they are useful!

Author: ProbablePrime
Source Code: https://github.com/ProbablePrime/nodecg-mixer 
License: GPL-2.0 License

#node #hook 

Hook into Beam Events and Emit Them to NodeCG Compatible Channels

Cómo Crear Un Formulario De Varios Pasos Solo Con UseState Hook

Los formularios son una parte esencial de la mayoría de las aplicaciones. Hay muchos paquetes disponibles en los diferentes paquetes para crear formularios. En este artículo, crearemos un formulario de varios pasos utilizando useStateHook sin el uso de paquetes de terceros y logrando la misma funcionalidad.

La principal prioridad del tutorial es analizar la lógica de un formulario de varios pasos. Nos saltaremos la mayor parte de la parte de estilo, así que si te quedas atascado en algún lugar, echa un vistazo a este repositorio .

Inicializar los archivos de inicio

Instalemos un nuevo proyecto Next.js ingresando este comando:

npx create-next-app mulitstep-form

Si lo prefiere, un proyecto React también funcionaría perfectamente bien. Para instalar un nuevo proyecto de React, ingrese lo siguiente:

npx create-react-app multistep-form 

El estilo es opcional, pero si desea seguirnos, usaremos Mantine CSS para este proyecto.

Instalar Mantine CSS

npm install @mantine/core @mantine/next @mantine/hooks

Siga la guía de configuración de Next.js aquí .

Next, let’s build some components for each step in our multi-step form.

Primero, construiremos el Formcomponente, que manejará la mayor parte de nuestra lógica de formulario. Continúe y cree un nuevo archivo en ./components/Form.js.

import { Box, Button, Title } from '@mantine/core';

function Form() {
  return (
    <Box
      sx={boxStyle}
    >
      <Title
        sx={{
          textAlign: 'center',
        }}
        order={2}
      >
        Hey there!
      </Title>
        {/* Steps */}
      <Button>Submit</Button>
    </Box>
  );
}
export default Form;

(Nota: aquí, el término "Pasos" simplemente significa las diferentes etapas del formulario de varios pasos)

A continuación, hagamos el primer paso de nuestro formulario: un mensaje introductorio simple.

Crear un nuevo archivo en ./components/First/index.js:

import { Box, Text, TextInput } from '@mantine/core';
function First() {
  return (
    <Box
      sx={boxStyle}
    >
      <Text>To start with, whats your beautiful name?</Text>
      <Box
        sx={{
          margin: '1rem 0',
        }}
      >
        <TextInput
          placeholder="John Doe"
          required
        />
      </Box>
    </Box>
  );
}
export default First;

Así es como debería verse su salida ahora:

Usar el useStateHook para moverse entre los componentes

A continuación, usaremos el useStateHook para movernos entre los diferentes pasos.

Solo hemos hecho un paso hasta ahora, pero siéntase libre de agregar sus propios pasos. A continuación se muestra cómo debería verse el flujo en este tutorial.

Ahora, con los componentes listos, agreguemos la lógica para cambiar entre componentes.

Inicializa un nuevo useStateHook, con su valor inicial como 0.

const [page, setPage] = useState(0);

Cree otra función que renderice condicionalmente una base de componente diferente en el pagevalor:

import First from './First';
import SecondStep from './Second';
import ThirdStep from './Third';

export default function Form () {
  const conditionalComponent = () => {
    switch (page) {
      case 0:
        return <First />;
      case 1:
        return <SecondStep />;
       case 2:
         return <ThirdStep />;
       default:
         return <First />;
    }
  }; 

return (
    <>
      <Box>
        {conditionalComponent()}
      </Box>
    </>
  )
}

A continuación, creemos un botón que, en onClick, incremente el valor de pageen 1:

//...imports
import { Button } from "@mantine/core"

export default function Form () {
  // stuff

  function handleSubmit () {
    //...stuff
  }

  return (
    <>
      {conditionalComponent()}
      <Button onClick={handleSubmit}>
        { page === 0 || page === 1 ? "Next" : "Submit" }
      </Button>
    </>
  )
}

Mientras tanto, en la handleSubmitfunción, incrementaremos la página. Más adelante en este artículo, agregaremos validaciones y usaremos la fetchAPI para enviar datos a una instancia de Supabase.

function handleSubmit () {
  setPage(page + 1);
}

Del mismo modo, necesitamos un botón y una función para volver al anterior page. El botón Atrás no se mostrará si el usuario está en la primera página, por lo tanto:

{
  page > 0 && <Button onClick={() => setPage(page - 1)}>Back</Button>
}

Seguimiento de los datos del formulario

Para realizar un seguimiento de la entrada del usuario, crearemos otro useStategancho.

const [formData, setFormData] = useState({
  name: '',
  email: '',
  employment_status: null
});

A continuación, pasaremos tanto formDatacomo setFormDataaccesorios a cada uno de nuestros componentes condicionales.

const conditionalComponent = () => {
   switch (page) {
    case 0:
       return <First formData={formData} setFormData={setFormData} />;
     case 1:
      return <SecondStep formData={formData} setFormData={setFormData} />;
    case 2:
      return <ThirdStep formData={formData} setFormData={setFormData} />;
    default:
      return <First formData={formData} setFormData={setFormData} />;
  }
};

Para obtener los datos del formulario de nuestros campos de entrada, haremos uso de la propiedad valuey onChangedentro de los Inputcampos.

import { Box, Text, TextInput } from '@mantine/core';

function First({ formData, setFormData }) {
  return (
    <Box
      sx={boxStyle}
    >
      <Text>To start with, whats your beautiful name?</Text>
      <Box>
        <TextInput
          onChange={(e) => {
            setFormData({
              ...formData,
              name: e.target.value,
            });
          }}
          value={formData.name}
          placeholder="John Doe"
          required
        />
      </Box>
    </Box>
  );
}

export default First;

Dos cosas están sucediendo aquí:

  • La propiedad contiene el valor actual del campo valuerespectivoInput
  • La onChangepropiedad se llama cada vez que el usuario intenta cambiar el Inputcampo

El selectcomponente se convierte en una entrada controlada de la siguiente manera:

<Select
    data={[
    { value: 'Student', label: 'Student' },
    { value: 'Employed', label: 'Employed' },
    { value: 'Business', label: 'Business' },
   ]}
    onChange={(e) => {
    console.log(e);
    setFormData({ ...formData, employment_status: e });
   }}
   value={formData.employment_status}
   sx={selectInputStyle}
   label="Your emplyment status"
   placeholder="Choose one"
/>

Aquí, en lugar de e.target.value, simplemente estamos pasando el eobjeto como un todo. Asegúrese de no pasar nada más que una cadena en el dataaccesorio dentro del Selectcomponente.

En nuestro último paso, simplemente mostraremos los datos del formulario para que el usuario los verifique y verifique y, si es necesario, le daremos la opción al usuario de regresar y cambiar la entrada ingresada anteriormente.

Dado que no tenemos ningún campo de entrada en el último componente, simplemente pase formDatay muéstrelos en consecuencia:

import { Box, Text } from '@mantine/core';
import Detail from '../Detail';

function ThirdStep({ formData }) {
  const boxStyle = {
    width: '70%',
    margin: '1rem auto',
    textAlign: 'center',
    padding: '1rem 0',
  };

  return (
    <Box sx={{ ...boxStyle, textAlign: 'left' }}>
      <Detail title="Name" data={formData.name} />
      <Detail title="Email" data={formData.email} />
      <Detail title="Employment status" data={formData.employment_status} />
    </Box>
  );
}
export default ThirdStep;

El Detailcomponente se ve así:

import { Box, Text } from '@mantine/core';

function Detail({ title, data }) {
  return (
    <Box
      sx={{
        margin: '10px 0',
      }}
    >
      <Text weight={300}>{title}</Text>
      <Text>{data}</Text>
    </Box>
  );
}

export default Detail;

Una vez hecho esto, su formulario de varios pasos ahora puede realizar un seguimiento de los datos del formulario sin problemas.

Agregar validación de formulario

A continuación, agregaremos la validación de formularios para aceptar datos válidos del usuario.

En la handleSubmitfunción, crearemos un if...elseflujo para verificar los campos de entrada en cada página y luego mover al usuario a la página siguiente.

function handleSubmit () {
  if (page === 0) {
    // do form validation
  } else if (page === 1) {
    // do form validation again
  } else if (page === 2) {
    // set page === 0 , and clear fields
  } else setPage(page + 1);
} 

En la primera ifdeclaración:

if (page === 0) {
  if (formData.name === '' || formData.name.length <= 1) {
    return alert('Please enter your name');
  } else {
    setPage(page + 1);
    console.log(formData);
  }
}

Aquí, si el namecampo está vacío o si la longitud es menor que 1, alertse muestra un cuadro.

De manera similar, agregamos la siguiente validación a la segunda página, que consta de un campo de dirección de correo electrónico y un campo para employment_status:

else if (page === 1) {
  if (formData.email === '' || !formData.email.includes('@')) {
    return alert('Please enter a valid email');
  } else if (!formData.employment_status) {
    return alert('Please select your employment status');
  } else {
    setPage(page + 1);
    console.log(formData);
  }
}

Aquí, primero verificaremos si el correo electrónico existe o si el correo electrónico contiene el símbolo "@". Si pasa la primera verificación, verificaremos si el employment_statuscampo contiene algo; de lo contrario, incrementamos pagey registramos los datos.

Para la última página, simplemente queremos mostrar los datos del formulario, por lo tanto, es suficiente establecer pageen 0 y borrar todos los campos de entrada.

else if (page === 2) {
  setPage(0);
  console.log(formData);
  setFormData({
     name: '',
     email: '',
     employment_status: null,
  });
}

¡Eso se encarga de toda nuestra validación!

Configuración de una instancia de Supabase

Enviemos los datos de nuestro formulario a una nueva instancia de Supabase. Supabase es una alternativa de código abierto a Firebase. Dirígete a Supabase para crear una nueva cuenta si eres nuevo.

Después de crear el proyecto, diríjase a la página de inicio de su proyecto recién creado y tome el anon keyy url.

(Nota: si no puede encontrar sus claves, intente encontrarlas en Configuración > API )

Con eso completo, ahora instale Supabase SDK, usando:

npm install @supabase/supabase-js

Cree un nuevo archivo ./lib/supabase.jsy exporte el cliente de Supabase, como se muestra a continuación.

import { createClient } from '@supabase/supabase-js';

export const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_KEY
)

Para las variables de entorno, cree un nuevo archivo en la raíz con el nombre .env.localy pase las claves.

NEXT_PUBLIC_SUPABASE_KEY=<Your key>
NEXT_PUBLIC_SUPABASE_URL=<Your url>

Envío de datos a una tabla Supabase

Vuelva a la instancia de Supabase recién creada y cree una nueva tabla en el editor de tablas.

Para manejar la lógica de Supabase, crearemos una ruta de API Next.js separada, por lo tanto, realizaremos una POSTsolicitud a través de la API Fetch nativa.

Continúe y cree una nueva ruta API Next.js en ./pages/api/post-supabase.js.

async function handler (req, res) {
  // server-side logic
}

export default handler;

Ahora, dado que tenemos una ruta API a través de la cual pasamos los datos de nuestro formulario, podemos agregar otra capa de protección para validar los datos de nuestro formulario. La validación del lado del cliente no siempre es confiable, por lo que siempre debe implementar la validación del lado del servidor antes de guardar los datos del usuario en la tabla de Supabase.

Dentro de la handlerfunción, primero verificaremos si el método de búsqueda es POST.

async function handler (req, res) {
  if(req.method === 'POST') {
    // post logic
    return res.status(200).json({message: "Success"});
  }
}

A continuación, desestructura todos los datos del formulario del cuerpo de la solicitud, como se muestra aquí:

if(req.method === "POST") {
  const { name, email, employment_status } = req.body;
}

A continuación, realizaremos la validación del lado del servidor:

if(req.method === "POST") {
  const { name, email, employment_status } = req.body;

    // server-side validation
    if (!name || name.length <= 3) {
      return res.status(400).json({
        status: 'fail',
        message: 'Please enter your name',
      });
    } else if (!email || !email.includes('@')) {
      return res.status(400).json({
        status: 'fail',
        message: 'Please enter a valid email',
      });
    } else if (!employment_status) {
      return res.status(400).json({
        status: 'fail',
        message: 'Please select your employment status',
      });
    }
}

En este caso, si alguien intenta alterar la validación del lado del cliente o realizar un ataque MITM, el servidor aún podrá negar los datos del formulario y devolver una respuesta de error.

Con la validación completa, todo lo que queda es enviar los datos del formulario a la tabla de Supabase. Para hacer esto, importe el cliente de Supabase y llame al insertmétodo.

import { supabase } from 'utils/supabase';

async function handler (req, res) {
  // ...server-side validation 
  try {
    await supabase.from('formdata').insert({
      name,
      email,
      employment_status,
    });
    return res
        .status(200)
        .json({ success: true, message: 'Post successful' });
  } catch (error) {
    console.log(error.response.data.message);
    return res.status(500).json({
        status: 'fail',
        message: 'Something went wrong',
      });
  }
}

.fromes la tabla en la que desea insertar los datos. Ahora que esto está hecho, haremos una POSTsolicitud a esta ruta API desde el lado del cliente cuando el usuario haga clic en el botón para enviar.

Regrese a la handleSubmitfunción, donde escribimos la lógica de envío para page === 2. Llamemos una solicitud de búsqueda a la ruta API /api/post-supabase:

try {
    await fetch('/api/post-supabase', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(formData),
  });
    setFormData({
    name: '',
    email: '',
    mployment_status: null,
    });
    setPage(0);
  } catch (error) {
    console.log(error);
}

Si el fetchmétodo se ejecuta como se esperaba, restableceremos los datos del formulario y los estableceremos pageen 0, y si algo sale mal, simplemente registraremos el error (también puede mostrar esto como una alerta).

Continúe y pruebe el formulario de varios pasos: debería poder ver una nueva entrada en la tabla de Supabase.

Conclusión

En esta publicación de blog, hemos aprendido cómo crear un formulario de varios pasos. Usando el useStateHook, hemos logrado crear formularios que pueden manipularse fácilmente.

Podemos crear formularios que tengan validación y puedan ser fácilmente reutilizados sin necesidad de crear nuevos formularios para diferentes casos de uso. Lo más importante es que hemos creado este formulario de varios pasos sin ningún paquete de terceros como React-Stepzilla.

Fuente: https://blog.logrocket.com/build-multi-step-form-usestate-hook/

 #usestate #hook #react 

Cómo Crear Un Formulario De Varios Pasos Solo Con UseState Hook
坂本  篤司

坂本 篤司

1651705500

useStateフックのみを使用してマルチステップフォームを作成する方法

フォームは、ほとんどのアプリケーションの重要な部分です。フォームを作成するためのさまざまなパッケージで利用できるパッケージがたくさんあります。useStateこの記事では、同じ機能を実現しながら、サードパーティのパッケージを使用せずにフックを使用してマルチステップフォームを作成します。

チュートリアルの主な優先事項は、マルチステップフォームのロジックを分析することです。スタイリング部分のほとんどをスキップするので、どこかで行き詰まった場合は、このリポジトリをチェックしてください。

開始ファイルの初期化

次のコマンドを入力して、新しいNext.jsプロジェクトをインストールしましょう。

npx create-next-app mulitstep-form

必要に応じて、Reactプロジェクトも完全に機能します。新しいReactプロジェクトをインストールするには、次のように入力します。

npx create-react-app multistep-form 

スタイリングはオプションですが、フォローしたい場合は、このプロジェクトにMantineCSSを使用します。

MantineCSSをインストールする

npm install @mantine/core @mantine/next @mantine/hooks

こちらのNext.jsのセットアップガイドに従ってください。

Next, let’s build some components for each step in our multi-step form.

まず、Formフォームロジックのほとんどを処理するコンポーネントを作成します。先に進み、で新しいファイルを作成します./components/Form.js

import { Box, Button, Title } from '@mantine/core';

function Form() {
  return (
    <Box
      sx={boxStyle}
    >
      <Title
        sx={{
          textAlign: 'center',
        }}
        order={2}
      >
        Hey there!
      </Title>
        {/* Steps */}
      <Button>Submit</Button>
    </Box>
  );
}
export default Form;

(注:ここで、「ステップ」という用語は、単にマルチステップフォームのさまざまなステージを意味します)

次に、フォームの最初のステップである簡単な紹介メッセージを作成しましょう。

で新しいファイルを作成します./components/First/index.js

import { Box, Text, TextInput } from '@mantine/core';
function First() {
  return (
    <Box
      sx={boxStyle}
    >
      <Text>To start with, whats your beautiful name?</Text>
      <Box
        sx={{
          margin: '1rem 0',
        }}
      >
        <TextInput
          placeholder="John Doe"
          required
        />
      </Box>
    </Box>
  );
}
export default First;

これはあなたの出力が今どのように見えるべきかです:

フックを使用してコンポーネント間を移動するuseState

次に、useStateフックを使用してさまざまなステップ間を移動します。

これまでは1つのステップしか実行していませんが、独自のステップを自由に追加してください。このチュートリアルでのフローは次のようになります。

コンポーネントの準備ができたら、コンポーネントを切り替えるロジックを追加しましょう。

初期値を0にして、新しいuseStateフックを初期化します。

const [page, setPage] = useState(0);

page値に基づいて異なるコンポーネントを条件付きでレンダリングする別の関数を作成します。

import First from './First';
import SecondStep from './Second';
import ThirdStep from './Third';

export default function Form () {
  const conditionalComponent = () => {
    switch (page) {
      case 0:
        return <First />;
      case 1:
        return <SecondStep />;
       case 2:
         return <ThirdStep />;
       default:
         return <First />;
    }
  }; 

return (
    <>
      <Box>
        {conditionalComponent()}
      </Box>
    </>
  )
}

onClick次に、の値をpage1ずつ増やすボタンを作成しましょう。

//...imports
import { Button } from "@mantine/core"

export default function Form () {
  // stuff

  function handleSubmit () {
    //...stuff
  }

  return (
    <>
      {conditionalComponent()}
      <Button onClick={handleSubmit}>
        { page === 0 || page === 1 ? "Next" : "Submit" }
      </Button>
    </>
  )
}

その間、handleSubmit関数では、ページをインクリメントします。この記事のさらに先では、検証を追加し、fetchAPIを使用してデータをSupabaseインスタンスに送信します。

function handleSubmit () {
  setPage(page + 1);
}

同様に、前に戻るためのボタンと関数が必要pageです。ユーザーが最初のページを表示している場合、戻るボタンは表示されません。したがって、次のようになります。

{
  page > 0 && <Button onClick={() => setPage(page - 1)}>Back</Button>
}

フォームデータを追跡する

ユーザー入力を追跡するために、別のuseStateフックを作成します。

const [formData, setFormData] = useState({
  name: '',
  email: '',
  employment_status: null
});

次に、両方formDatasetFormData小道具として各条件付きコンポーネントに渡します。

const conditionalComponent = () => {
   switch (page) {
    case 0:
       return <First formData={formData} setFormData={setFormData} />;
     case 1:
      return <SecondStep formData={formData} setFormData={setFormData} />;
    case 2:
      return <ThirdStep formData={formData} setFormData={setFormData} />;
    default:
      return <First formData={formData} setFormData={setFormData} />;
  }
};

入力フィールドからフォームデータを取得するには、フィールド内のvalueandonChangeプロパティを使用しInputます。

import { Box, Text, TextInput } from '@mantine/core';

function First({ formData, setFormData }) {
  return (
    <Box
      sx={boxStyle}
    >
      <Text>To start with, whats your beautiful name?</Text>
      <Box>
        <TextInput
          onChange={(e) => {
            setFormData({
              ...formData,
              name: e.target.value,
            });
          }}
          value={formData.name}
          placeholder="John Doe"
          required
        />
      </Box>
    </Box>
  );
}

export default First;

ここでは2つのことが起こっています。

  • valueプロパティには、それぞれのInputフィールドの現在の値が含まれます
  • プロパティは、ユーザーがフィールドonChangeを変更しようとするたびに呼び出されますInput

コンポーネントは、select次のように制御された入力になります。

<Select
    data={[
    { value: 'Student', label: 'Student' },
    { value: 'Employed', label: 'Employed' },
    { value: 'Business', label: 'Business' },
   ]}
    onChange={(e) => {
    console.log(e);
    setFormData({ ...formData, employment_status: e });
   }}
   value={formData.employment_status}
   sx={selectInputStyle}
   label="Your emplyment status"
   placeholder="Choose one"
/>

ここでは、の代わりに、オブジェクト全体e.target.valueを渡すだけです。コンポーネント内の支柱にe文字列以外のものを渡さないように注意してください。dataSelect

最後のステップでは、ユーザーがクロスチェックして確認できるようにフォームデータを表示し、必要に応じて、前に入力した入力に戻って変更するオプションをユーザーに提供します。

最後のコンポーネントには入力フィールドがないため、を渡してformDataそれに応じて表示します。

import { Box, Text } from '@mantine/core';
import Detail from '../Detail';

function ThirdStep({ formData }) {
  const boxStyle = {
    width: '70%',
    margin: '1rem auto',
    textAlign: 'center',
    padding: '1rem 0',
  };

  return (
    <Box sx={{ ...boxStyle, textAlign: 'left' }}>
      <Detail title="Name" data={formData.name} />
      <Detail title="Email" data={formData.email} />
      <Detail title="Employment status" data={formData.employment_status} />
    </Box>
  );
}
export default ThirdStep;

コンポーネントは次のDetailようになります。

import { Box, Text } from '@mantine/core';

function Detail({ title, data }) {
  return (
    <Box
      sx={{
        margin: '10px 0',
      }}
    >
      <Text weight={300}>{title}</Text>
      <Text>{data}</Text>
    </Box>
  );
}

export default Detail;

これで、マルチステップフォームでフォームデータをシームレスに追跡できるようになります。

フォーム検証の追加

次に、フォーム検証を追加して、ユーザーからの有効なデータを受け入れます。

この関数では、各ページの入力フィールドを確認し、その後ユーザーを次のページに移動するフローをhandleSubmit作成します。if...else

function handleSubmit () {
  if (page === 0) {
    // do form validation
  } else if (page === 1) {
    // do form validation again
  } else if (page === 2) {
    // set page === 0 , and clear fields
  } else setPage(page + 1);
} 

最初のifステートメントで:

if (page === 0) {
  if (formData.name === '' || formData.name.length <= 1) {
    return alert('Please enter your name');
  } else {
    setPage(page + 1);
    console.log(formData);
  }
}

ここで、nameフィールドが空の場合、または長さが1未満の場合、alertボックスが表示されます。

同様に、次の検証を2番目のページに追加します。これは、電子メールアドレスフィールドと次のフィールドで構成されていますemployment_status

else if (page === 1) {
  if (formData.email === '' || !formData.email.includes('@')) {
    return alert('Please enter a valid email');
  } else if (!formData.employment_status) {
    return alert('Please select your employment status');
  } else {
    setPage(page + 1);
    console.log(formData);
  }
}

ここでは、最初に電子メールが存在するかどうか、または電子メールに「@」記号が含まれているかどうかを確認します。最初のチェックに合格すると、employment_statusフィールドに何かが含まれているかどうかをチェックします。それ以外の場合は、データをインクリメントpageしてログに記録します。

最後のページでは、フォームデータを表示するだけなので、pageを0に設定して、すべての入力フィールドをクリアするだけで十分です。

else if (page === 2) {
  setPage(0);
  console.log(formData);
  setFormData({
     name: '',
     email: '',
     employment_status: null,
  });
}

これですべての検証が完了します。

Supabaseインスタンスの設定

フォームデータを新しいSupabaseインスタンスに送信しましょう。Supabaseは、Firebaseに代わるオープンソースです。初めての場合は、 Supabaseにアクセスして新しいアカウントを作成してください。

プロジェクトを作成したら、新しく作成したプロジェクトのホームページに移動して、とを取得しanon keyますurl

(注:キーが見つからない場合は、 [設定] > [ API ]でキーを見つけてみてください)

これが完了したら、次を使用してSupabaseSDKをインストールします。

npm install @supabase/supabase-js

以下に示すように、で新しいファイルを作成し./lib/supabase.js、Supabaseクライアントをエクスポートします。

import { createClient } from '@supabase/supabase-js';

export const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_KEY
)

環境変数については、ルートに名前を付けて新しいファイルを作成.env.localし、キーを渡します。

NEXT_PUBLIC_SUPABASE_KEY=<Your key>
NEXT_PUBLIC_SUPABASE_URL=<Your url>

Supabaseテーブルへのデータの送信

新しく作成されたSupabaseインスタンスに戻り、テーブルエディタで新しいテーブルを作成します。

Supabaseロジックを処理するために、別のNext.js APIルートを作成しPOST、ネイティブのFetchAPIを介してリクエストを行います。

先に進み、で新しいNext.jsAPIルートを作成し./pages/api/post-supabase.jsます。

async function handler (req, res) {
  // server-side logic
}

export default handler;

これで、フォームデータを渡すAPIルートがあるので、フォームデータを検証するための保護の別のレイヤーを追加できます。クライアント側の検証は常に信頼できるとは限らないため、ユーザーデータをSupabaseテーブルに保存する前に、常にサーバー側の検証も実装する必要があります。

関数内ではhandler、最初に、fetchメソッドがであるかどうかを確認しPOSTます。

async function handler (req, res) {
  if(req.method === 'POST') {
    // post logic
    return res.status(200).json({message: "Success"});
  }
}

次に、次に示すように、リクエスト本文からすべてのフォームデータを分解します。

if(req.method === "POST") {
  const { name, email, employment_status } = req.body;
}

次に、サーバー側の検証を実行します。

if(req.method === "POST") {
  const { name, email, employment_status } = req.body;

    // server-side validation
    if (!name || name.length <= 3) {
      return res.status(400).json({
        status: 'fail',
        message: 'Please enter your name',
      });
    } else if (!email || !email.includes('@')) {
      return res.status(400).json({
        status: 'fail',
        message: 'Please enter a valid email',
      });
    } else if (!employment_status) {
      return res.status(400).json({
        status: 'fail',
        message: 'Please select your employment status',
      });
    }
}

この場合、誰かがクライアント側の検証を改ざんしたり、MITM攻撃を実行したりしようとしても、サーバーはフォームデータを拒否し、エラー応答を返すことができます。

検証が完了すると、あとはフォームデータをSupabaseテーブルに送信するだけです。これを行うには、Supabaseクライアントをインポートし、insertメソッドを呼び出します。

import { supabase } from 'utils/supabase';

async function handler (req, res) {
  // ...server-side validation 
  try {
    await supabase.from('formdata').insert({
      name,
      email,
      employment_status,
    });
    return res
        .status(200)
        .json({ success: true, message: 'Post successful' });
  } catch (error) {
    console.log(error.response.data.message);
    return res.status(500).json({
        status: 'fail',
        message: 'Something went wrong',
      });
  }
}

.fromデータを挿入するテーブルです。これでPOST、ユーザーがボタンをクリックして送信したときに、クライアント側からこのAPIルートにリクエストを送信します。

handleSubmitの送信ロジックを記述した関数に戻りpage === 2ます。APIルートへのフェッチリクエストを呼び出しましょう/api/post-supabase

try {
    await fetch('/api/post-supabase', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(formData),
  });
    setFormData({
    name: '',
    email: '',
    mployment_status: null,
    });
    setPage(0);
  } catch (error) {
    console.log(error);
}

メソッドが期待どおりに実行された場合はfetch、フォームデータをリセットしpageて0に設定し、問題が発生した場合は、エラーをログに記録します(これをアラートとして表示することもできます)。

先に進んで、マルチステップフォームを試してください。Supabaseテーブルに新しいエントリが表示されるはずです。

結論

このブログ投稿では、マルチステップフォームを作成する方法を学びました。フックを使用して、useState簡単に操作できるフォームを作成することができました。

検証があり、さまざまなユースケースの新しいフォームを作成しなくても簡単に再利用できるフォームを作成できます。最も重要なことは、React-Stepzillaのようなサードパーティのパッケージを使用せずにこのマルチステップフォームを構築したことです。

ソース:https ://blog.logrocket.com/build-multi-step-form-usestate-hook/

#usestate #hook #react 

useStateフックのみを使用してマルチステップフォームを作成する方法

Collection of Flutter Hooks

Collection of Flutter Hooks

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add flutter_use_geolocation

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  flutter_use_geolocation: ^0.0.2

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:flutter_use_geolocation/flutter_use_geolocation.dart';

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_use_geolocation/use_geolocation.dart';
import 'package:geolocator/geolocator.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class SampleError extends Error {
  SampleError(this.message);
  final String message;
}

class UseError extends Error {}

class SampleException implements Exception {}

class UseException implements Exception {}

class MyHomePage extends HookWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    debugPrint("build");

    final geolocation = useGeolocation();
    return Scaffold(
      body: SingleChildScrollView(
        padding: const EdgeInsets.symmetric(vertical: 32),
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              const Text("-- Geolocation --"),
              Text("permission checked: ${geolocation.fetched}"),
              Text("location: ${geolocation.position}"),
              ElevatedButton(
                onPressed: () async {
                  await Geolocator.requestPermission();
                },
                child: const Text('Grant Location permission'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Download Details:

Author: wasabeef

Source Code: https://github.com/wasabeef/flutter_use

#flutter #hook 

Collection of Flutter Hooks
Royce  Reinger

Royce Reinger

1649499000

Release-it: Automate Versioning and Package Publishing

Release It! 🚀

🚀 Generic CLI tool to automate versioning and package publishing related tasks:

Use release-it for version management and publish to anywhere with its versatile configuration, a powerful plugin system, and hooks to execute any command you need to test, build, and/or publish your project. 

Installation

Although release-it is a generic release tool, installation requires npm. To use release-it, a package.json file is not required. The recommended way to install release-it also adds basic configuration. Answer one or two questions and it's ready:

npm init release-it

Alternatively, install it manually, and add the release script to package.json:

npm install --save-dev release-it
{
  "name": "my-package",
  "version": "1.0.0",
  "scripts": {
    "release": "release-it"
  },
  "devDependencies": {
    "release-it": "*"
  }
}

Now you can run npm run release from the command line (any release-it arguments behind the --):

npm run release
npm run release -- minor --ci

Global usage

Use release-it in any (non-npm) project, take it for a test drive, or install it globally:

# Run release-it from anywhere (without installation)
npx release-it

# Install globally and run from anywhere
npm install --global release-it
release-it

Usage

Release a new version:

release-it

You will be prompted to select the new version, and more prompts will follow based on your setup.

Run release-it from the root of the project to prevent potential issues.

Dry Runs

Use --dry-run to show the interactivity and the commands it would execute.

→ See Dry Runs for more details.

To print the next version without releasing anything, add the --release-version flag.

Configuration

Out of the box, release-it has sane defaults, and plenty of options to configure it. Most projects use a .release-it.json in the project root, or a release-it property in package.json.

→ See Configuration for more details.

Here's a quick example .release-it.json:

{
  "git": {
    "commitMessage": "chore: release v${version}"
  },
  "github": {
    "release": true
  }
}

Interactive vs. CI mode

By default, release-it is interactive and allows you to confirm each task before execution:

By using the --ci option, the process is fully automated without prompts. The configured tasks will be executed as demonstrated in the first animation above. On a Continuous Integration (CI) environment, this non-interactive mode is activated automatically.

Use --only-version to use a prompt only to determine the version, and automate the rest.

Latest version

How does release-it determine the latest version?

  1. For projects with a package.json, its version will be used (see npm to skip this).
  2. Otherwise, release-it uses the latest Git tag to determine which version should be released.
  3. As a last resort, 0.0.0 will be used as the latest version.

Alternatively, a plugin can be used to override this (e.g. to manage a VERSION or composer.json file):

Add the --release-version flag to print the next version without releasing anything.

Git

Git projects are supported well by release-it, automating the tasks to stage, commit, tag and push releases to any Git remote.

→ See Git for more details.

GitHub Releases

GitHub projects can have releases attached to Git tags, containing release notes and assets. There are two ways to add GitHub releases in your release-it flow:

  1. Automated (requires a GITHUB_TOKEN)
  2. Manual (using the GitHub web interface with pre-populated fields)

→ See GitHub Releases for more details.

GitLab Releases

GitLab projects can have releases attached to Git tags, containing release notes and assets. To automate GitLab releases:

→ See GitLab Releases for more details.

Changelog

By default, release-it generates a changelog, to show and help select a version for the new release. Additionally, this changelog serves as the release notes for the GitHub or GitLab release.

The default command is based on git log .... This setting (git.changelog) can be overridden. To further customize the release notes for the GitHub or GitLab release, there's github.releaseNotes or gitlab.releaseNotes. Make sure any of these commands output the changelog to stdout. Plugins are available for:

  • GitHub and GitLab Releases
  • auto-changelog
  • Conventional Changelog
  • Keep A Changelog

→ See Changelog for more details.

Publish to npm

With a package.json in the current directory, release-it will let npm bump the version in package.json (and package-lock.json if present), and publish to the npm registry.

→ See Publish to npm for more details.

Manage pre-releases

With release-it, it's easy to create pre-releases: a version of your software that you want to make available, while it's not in the stable semver range yet. Often "alpha", "beta", and "rc" (release candidate) are used as identifier for pre-releases. An example pre-release version is 2.0.0-beta.0.

→ See Manage pre-releases for more details.

Update or re-run existing releases

Use --no-increment to not increment the last version, but update the last existing tag/version.

This may be helpful in cases where the version was already incremented. Here's a few example scenarios:

  • To update or publish a (draft) GitHub Release for an existing Git tag.
  • Publishing to npm succeeded, but pushing the Git tag to the remote failed. Then use release-it --no-increment --no-npm to skip the npm publish and try pushing the same Git tag again.

Hooks

Use script hooks to run shell commands at any moment during the release process (such as before:init or after:release).

The format is [prefix]:[hook] or [prefix]:[plugin]:[hook]:

partvalue
prefixbefore or after
pluginversion, git, npm, github, gitlab
hookinit, bump, release

Use the optional :plugin part in the middle to hook into a life cycle method exactly before or after any plugin.

The core plugins include version, git, npm, github, gitlab.

Note that hooks like after:git:release will not run when either the git push failed, or when it is configured not to be executed (e.g. git.push: false). See execution order for more details on execution order of plugin lifecycle methods.

All commands can use configuration variables (like template strings). An array of commands can also be provided, they will run one after another. Some example release-it configuration:

{
  "hooks": {
    "before:init": ["npm run lint", "npm test"],
    "after:my-plugin:bump": "./bin/my-script.sh",
    "after:bump": "npm run build",
    "after:git:release": "echo After git push, before github release",
    "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}."
  }
}

The variables can be found in the default configuration. Additionally, the following variables are exposed:

version
latestVersion
changelog
name
repo.remote, repo.protocol, repo.host, repo.owner, repo.repository, repo.project

All variables are available in all hooks. The only exception is that the additional variables listed above are not yet available in the init hook.

Use --verbose to log the output of the commands.

For the sake of verbosity, the full list of hooks is actually: init, beforeBump, bump, beforeRelease, release or afterRelease. However, hooks like before:beforeRelease look weird and are usually not useful in practice.

Plugins

Since v11, release-it can be extended in many, many ways. Here are some plugins:

PluginDescription
@release-it/bumperRead & write the version from/to any file
@release-it/conventional-changelogProvides recommended bump, conventional-changelog, and updates CHANGELOG.md
@release-it/keep-a-changelogMaintain CHANGELOG.md using the Keep a Changelog standards
release-it-lerna-changelogIntegrates lerna-changelog into the release-it pipeline
release-it-yarn-workspacesReleases each of your projects configured workspaces
release-it-calver-pluginEnables Calendar Versioning (calver) with release-it
@grupoboticario/news-fragmentsAn easy way to generate your changelog file
@j-ulrich/release-it-regex-bumperRegular expression based version read/write plugin for release-it

Internally, release-it uses its own plugin architecture (for Git, GitHub, GitLab, npm).

→ See all release-it plugins on npm.

→ See plugins for documentation to write plugins.

Distribution repository

Deprecated. Please see distribution repository for more details.

Metrics

Use --disable-metrics to opt-out of sending some anonymous statistical data to Google Analytics. For details, refer to lib/metrics.js. Please consider to not opt-out: more data means more support for future development.

Troubleshooting & debugging

  • With release-it --verbose (or -V), release-it prints the output of every user-defined hook.
  • With release-it -VV, release-it also prints the output of every internal command.
  • Use DEBUG=release-it:* release-it [...] to print configuration and more error details.

Use verbose: 2 in a configuration file to have the equivalent of -VV on the command line.

Use release-it programmatically

While mostly used as a CLI tool, release-it can be used as a dependency to integrate in your own scripts. See use release-it programmatically for example code.

Example projects using release-it

Resources

Links

Author: Release-it
Source Code: https://github.com/release-it/release-it 
License: MIT License

#git #github #cli #hook #automated 

Release-it: Automate Versioning and Package Publishing
Royce  Reinger

Royce Reinger

1649442360

Quickhook: Faster Git Hook (pre-commit, Etc.) Runner

quickhook

Quickhook is a fast, Unix'y, opinionated Git hook runner. It handles running all user-defined hooks, collecting their output, reporting failures, and exiting with a non-zero status code if appropriate.

Installation

If you're on Mac there is a Homebrew tap for Quickhook:

brew tap dirk/quickhook
# Tapped 1 formula (28 files, 28KB).

brew install quickhook
# /usr/local/Cellar/quickhook/1.4.0: 3 files, 3.7MB, built in 9 seconds

Usage

First you'll need to set Quickhook to be called in your Git hooks. The quickhook install command will discover hooks defined in the .quickhook directory and create Git hook shims for those. For example, the below is what you can expect if you clone this repository and install the shims:

$ quickhook install
Create file .git/hooks/commit-msg? [yn] y
Installed shim .git/hooks/commit-msg
Create file .git/hooks/pre-commit? [yn] y
Installed shim .git/hooks/pre-commit

To make it easier to test hooks outside of the normal Git paths the hook sub-commands provide some additional options. For example, these are some of the options you can use with the pre-commit hook command:

# Run the pre-commit hooks on all Git-tracked files in the repository
quickhook hook pre-commit --all

# Run them on just one or more files
quickhook hook pre-commit --files hooks/commit_msg.go hooks/pre_commit.go

You can see all of the options by passing --help to the sub-command:

$ quickhook hook pre-commit --help
...
OPTIONS:
   --all, -a    Run on all Git-tracked files
   --files, -F  Run on the given comma-separated list of files

Writing hooks

Quickhook will look for hooks in a corresponding sub-directory of the .quickhook directory in your repository. For example, it will look for pre-commit hooks in .quickhook/pre-commit/. A hook is any executable file in that directory. See the go-vet file for an example.

pre-commit

Pre-commit hooks receive the list of staged files separated by newlines on stdin. They are expected to write their result to stdout/stderr (Quickhook doesn't care). If they exit with a non-zero exit code then the commit will be aborted and their output displayed to the user.

Note: Pre-commit hooks will be executed in parallel and should not mutate the local repository state.

File-and-line-specific errors should be written in the following format:

some/directory/and/file.go:123: Something doesn't look right

A more formal definition of an error line is:

  • Sequence of characters representing a valid path
  • A colon (:) character
  • Integer of the line where the error occurred
  • A color character followed by a space character
  • Any printable character describing the error
  • A newline (\n) terminating the error line

This informal Unix convention is already followed by many programming languages, linters, and so forth.

commit-msg

Commit-message hooks are run sequentially. They receive a single argument: a path to a temporary file containing the message for the commit. If they exit with a non-zero exit code the commit will be aborted and any stdout/stderr output displayed to the user.

Given that they are run sequentially, commit-msg hooks are allowed to mutate the commit message temporary file.

Performance

Quickhook is designed to be as fast and lightweight as possible. There are a few guiding principles for this:

  • Ship as a small, self-contained executable.
  • Eschew configuration in favor of rigid adherence to Unix'y approach of composing programs.
  • Do as much as possible in parallel.

Author: Dirk
Source Code: https://github.com/dirk/quickhook/ 
License: BSD-3-Clause License

#git #hook 

Quickhook: Faster Git Hook (pre-commit, Etc.) Runner
Royce  Reinger

Royce Reinger

1649434943

Overcommit: A Fully Configurable and Extendable Git Hook Manager

overcommit is a tool to manage and configure Git hooks.

In addition to supporting a wide variety of hooks that can be used across multiple repositories, you can also define hooks specific to a repository which are stored in source control. You can also easily add your existing hook scripts without writing any Ruby code.

Requirements

This project aims to support the following Ruby runtimes on both *nix and Windows:

  • Ruby 2.4+

Windows

If you are using Overcommit on Windows, make sure you include the ffi gem in your list of dependencies. Overcommit does not include the ffi gem by default since it significantly increases the install time for non-Windows platforms.

Dependencies

Some hooks have third-party dependencies. For example, to lint your SCSS files, you're going to need the scss_lint gem.

Depending on the hooks you enable/disable for your repository, you'll need to ensure your development environment already has those dependencies installed. Most hooks will display a warning if a required executable isn't available.

If you are using Bundler to manage your Ruby gem dependencies, you'll likely want to use the gemfile option to control which gem versions are available during your hook runs.

Installation

overcommit is installed via RubyGems. It is strongly recommended that your environment support running gem install without requiring root user privileges via sudo or otherwise. Using a Ruby version manager like rbenv or rvm is recommended.

Once you have an environment that allows you to install gems without sudo, run:

gem install overcommit

You can then run the overcommit command to install hooks into repositories.

mkdir important-project
cd important-project
git init
overcommit --install

After running overcommit --install, any existing hooks for your repository which Overcommit will replace will be backed up. You can restore everything to the way it was by running overcommit --uninstall.

Automatically Install Overcommit Hooks

If you want to use overcommit for all repositories you create/clone going forward, add the following to automatically run in your shell environment:

export GIT_TEMPLATE_DIR="$(overcommit --template-dir)"

The GIT_TEMPLATE_DIR provides a directory for Git to use as a template for automatically populating the .git directory. If you have your own template directory, you might just want to copy the contents of overcommit --template-dir to that directory.

Usage

Once you've installed the hooks via overcommit --install, they will automatically run when the appropriate hook is triggered.

The overcommit executable supports the following command-line flags:

Command Line FlagDescription
-i/--installInstall Overcommit hooks in a repository
-u/--uninstallRemove Overcommit hooks from a repository
-f/--forceDon't bail on install if other hooks already exist--overwrite them
-l/--list-hooksDisplay all available hooks in the current repository
-r/--runRun pre-commit hook against all tracked files in repository
-t/--template-dirPrint location of template directory
-h/--helpShow command-line flag documentation
-v/--versionShow version

Skipping Hooks

Sometimes a hook will report an error that for one reason or another you'll want to ignore. To prevent these errors from blocking your commit, you can include the name of the relevant hook in the SKIP environment variable, e.g.

SKIP=RuboCop git commit

If you would prefer to specify a whitelist of hooks rather than a blacklist, use the ONLY environment variable instead.

ONLY=RuboCop git commit

Use this feature sparingly, as there is no point to having the hook in the first place if you're just going to ignore it. If you want to ensure a hook is never skipped, set the required option to true in its configuration. If you attempt to skip it, you'll see a warning telling you that the hook is required, and the hook will still run.

Disabling Overcommit

If you have scripts that execute git commands where you don't want Overcommit hooks to run, you can disable Overcommit entirely by setting the OVERCOMMIT_DISABLE environment variable.

OVERCOMMIT_DISABLE=1 ./my-custom-script

Disabling Colorized Output

Overcommit automatically colorizes its output based on whether it is outputting to a TTY. However, you can manually enable/disable color by setting the OVERCOMMIT_COLOR environment variable.

OVERCOMMIT_COLOR=0 git commit

Continuous Integration

You can run the same set of hooks that would be executed in a pre-commit hook against your entire repository by running overcommit --run. This makes it easy to have the checks verified by a CI service such as Travis CI, including custom hooks you've written yourself.

The --run flag works by creating a pre-commit context that assumes all the files in your repository have changed, and follows the same rules as a normal pre-commit check. If any hook fails with an error, it will return a non-zero exit code.

Configuration

Overcommit provides a flexible configuration system that allows you to tailor the built-in hooks to suit your workflow. All configuration specific to a repository is stored in .overcommit.yml in the top-level directory of the repository.

When writing your own configuration, it will automatically extend the default configuration, so you only need to specify your configuration with respect to the default. In order to enable/disable hooks, you can add the following to your repo-specific configuration file:

PreCommit:
  RuboCop:
    enabled: true
    command: ['bundle', 'exec', 'rubocop'] # Invoke within Bundler context

Hook Options

Individual hooks expose both built-in configuration options as well as their own custom options unique to each hook. The following table lists all built-in configuration options:

OptionDescription
enabledIf false, this hook will never be run
requiredIf true, this hook cannot be skipped via the SKIP environment variable
quietIf true, this hook does not display any output unless it warns/fails
descriptionMessage displayed while hook is running.
requires_filesIf true, this hook runs only if files that are applicable to it have been modified. See include and exclude for how to specify applicable files.
includeFile paths or glob patterns of files that apply to this hook. The hook will only run on the applicable files when they have been modified. Note that the concept of modified varies for different types of hooks. By default, include matches every file until you specify a list of patterns.
excludeFile paths or glob patterns of files that do not apply to this hook. This is used to exclude any files that would have been matched by include.
exclude_branchesList of branch names or glob patterns of branches that this hook should not run against.
exclude_remotesPrePush hooks only. List of remote names that the hook should not run against.
include_remote_ref_deletionsPrePush hooks only. By default, PrePush hooks will not run for pushes that delete a remote ref (i.e. branches or tags). Set to true to have the hook run even for deleted remote ref.
problem_on_unmodified_lineHow to treat errors reported on lines that weren't modified during the action captured by this hook (e.g. for pre-commit hooks, warnings/errors reported on lines that were not staged with git add may not be warnings/errors you care about). Valid values are report: report errors/warnings as-is regardless of line location (default); warn: report errors as warnings if they are on lines you didn't modify; and ignore: don't display errors/warnings at all if they are on lines you didn't modify (ignore is not recommended).
on_failChange the status of a failed hook to warn or pass. This allows you to treat failures as warnings or potentially ignore them entirely, but you should use caution when doing so as you might be hiding important information.
on_warnSimilar to on_fail, change the status of a hook that returns a warning status to either pass (you wish to silence warnings entirely) or fail (you wish to treat all warnings as errors).
required_executableName of an executable that must exist in order for the hook to run. If this is a path (e.g. ./bin/ruby), ensures that the executable file exists at the given location relative to the repository root. Otherwise, if it just the name of an executable (e.g. ruby) checks if the executable can be found in one of the directories in the PATH environment variable. Set this to a specific path if you want to always use an executable that is stored in your repository. (e.g. RubyGems bin stubs, Node.js binaries, etc.)
required_library/required_librariesList of Ruby libraries to load with Kernel.require before the hook runs. This is specifically for hooks that integrate with external Ruby libraries.
commandArray of arguments to use as the command. How each hook uses this is different, but it allows hooks to change the context with which they run. For example, you can change the command to be ['bundle', 'exec', 'rubocop'] instead of just rubocop so that you can use the gem versions specified in your local Gemfile.lock. This defaults to the name of the required_executable.
flagsArray of arguments to append to the command. This is useful for customizing the behavior of a tool. It's also useful when a newer version of a tool removes/renames existing flags, so you can update the flags via your .overcommit.yml instead of waiting for an upstream fix in Overcommit.
envHash of environment variables the hook should be run with. This is intended to be used as a last resort when an executable a hook runs is configured only via an environment variable. Any pre-existing environment variables with the same names as ones defined in env will have their original values restored after the hook runs. NOTE: Currently, only strings are accepted values. Boolean values will raise an error. WARNING: If you set the same environment variable for multiple hooks and you've enabled parallel hook runs, since the environment is shared across all threads you could accidentally have these separate hooks trample on each other. In this case, you should disable parallelization for the hook using the parallelize option.
parallelizeWhether to allow this hook to be run concurrently with other hooks. Disable this if the hook requires access to a shared resource that other hooks may also access and modify (e.g. files, the git index, process environment variables, etc).
processorsThe number of processing units to reserve for this hook. This does not reserve CPUs, but indicates that out of the total number of possible concurrent hooks allowed by the global concurrency option, this hook requires the specified number. Thus in the typical case where concurrency is set to the number of available cores (default), and you have a hook that executes an application which itself creates 2 threads (or is otherwise scheduled on 2 cores), you can indicate that Overcommit should allocate 2 processors to the hook. Ideally this means your hooks won't put undue load on your available cores.
install_commandCommand the user can run to install the required_executable (or alternately the specified required_libraries). This is intended for documentation purposes, as Overcommit does not install software on your behalf since there are too many edge cases where such behavior would result in incorrectly configured installations (e.g. installing a Python package in the global package space instead of in a virtual environment).
skip_file_checkoutWhether to skip this hook for file checkouts (e.g. git checkout some-ref -- file). Only applicable to PostCheckout hooks.
skip_ifArray of arguments to be executed to determine whether or not the hook should run. For example, setting this to a value of ['bash', '-c', '! which my-executable'] would allow you to skip running this hook if my-executable was not in the bin path.

In addition to the built-in configuration options, each hook can expose its own unique configuration options. The AuthorEmail hook, for example, allows you to customize the regex used to check commit author emails via the pattern option—useful if you want to enforce that developers use a company email address for their commits. This provides incredible flexibility for hook authors as you can make your hooks sufficiently generic and then customize them on a per-project basis.

Hook Categories

Hook configurations are organized into categories based on the type of hook. So pre-commit hooks are located under the PreCommit option, and post-commit hooks are located under PostCommit. See the default configuration for a thorough example.

The ALL Hook

Within a hook category, there is a special type of hook configuration that applies to all hooks in the category. This configuration looks like a normal hook configuration, except it has the name ALL:

PreCommit:
  ALL:
    problem_on_unmodified_line: warn
    requires_files: true
    required: false
    quiet: false

  SomeHook:
    enabled: true

  ...

The ALL configuration is useful for when you want to DRY up your configuration, or when you want to apply changes across an entire category of hooks.

Note that array configuration options (like include/exclude) in the special ALL hook section are not merged with individual hook configurations if custom ones are defined for the hook. Any custom configuration option for include/exclude will replace the ALL hook's configuration. If you want to have a global list of default exclusions and extend them with a custom list, you can use YAML references, e.g.

PreCommit:
  ALL:
    exclude: &default_excludes
      - 'node_modules/**/*'
      - 'vendor/**/*'
  MyHook:
    exclude:
      - *default_excludes
      - 'another/directory/in/addition/to/default/excludes/**/*'

Again, you can consult the default configuration for detailed examples of how the ALL hook can be used.

Gemfile

You may want to enforce the version of Overcommit or other gems that you use in your git hooks. This can be done by specifying the gemfile option in your .overcommit.yml.

The gemfile option tells Overcommit to load the specified file with Bundler, the standard gem dependency manager for Ruby. This is useful if you would like to:

  • Enforce a specific version of Overcommit to use for all hook runs (or to use a version from the master branch that has not been released yet)
  • Enforce a specific version or unreleased branch is used for a gem you want to use in your git hooks

Loading a Bundler context necessarily adds a startup delay to your hook runs as Bundler parses the specified Gemfile and checks that the dependencies are satisfied. Thus for projects with many gems this can introduce a noticeable delay.

The recommended workaround is to create a separate Gemfile in the root of your repository (call it .overcommit_gems.rb), and include only the gems that your Overcommit hooks need in order to run. Generate the associated lock file by running:

bundle install --gemfile=.overcommit_gems.rb

...and commit .overcommit_gems.rb and the resulting .overcommit_gems.rb.lock file to your repository. Set your gemfile option to .overcommit_gems.rb, and you're all set.

Using a smaller Gemfile containing only the gems used by your Overcommit hooks significantly reduces the startup delay in your hook runs. It is thus the recommended approach unless your project has a relatively small number of gems in your Gemfile.

Plugin Directory

You can change the directory that project-specific hooks are loaded from via the plugin_directory option. The default directory is .git-hooks.

Quiet Hook Runs

If you prefer to have your hooks be completely silent unless there is a problem, you can set the top-level quiet option to true. Note that if you have many hooks or slow hooks this may not be desirable, as you don't get visual feedback indicating the general progress of the hook run.

Concurrency

Overcommit runs hooks in parallel by default, with a number of concurrent workers equal to the number of logical cores on your machine. If you know your particular set of hooks would benefit from higher/lower number of workers, you can adjust the global concurrency option. You can define single-operator mathematical expressions, e.g. %{processors} * 2, or %{processors} / 2.

concurrency: '%{processors} / 4'

Note that individual hooks can specify the number of processors they require with the processors hook option. See the hook options section for more details.

Signature Verification

You can disable manual verification of signatures by setting verify_signatures to false. See the Security section for more information on this option and what exactly it controls.

Built-In Hooks

Currently, Overcommit supports the following hooks out of the box—simply enable them in your .overcommit.yml.

Note: Hooks with a * are enabled by default.

Warning: This list represents the list of hooks available on the master branch. Please consult the change log to view which hooks have not been released yet.

CommitMsg

commit-msg hooks are run against every commit message you write before a commit is created. A failed hook prevents a commit from being created. These hooks are useful for enforcing policies on your commit messages, e.g. ensuring a task ID is included for tracking purposes, or ensuring your commit messages follow proper formatting guidelines.

PostCheckout

post-checkout hooks run after a successful git checkout, or in other words any time your HEAD changes or a file is explicitly checked out.

PostCommit

post-commit hooks run after a commit is successfully created. A hook failing in this case does not prevent the commit since it has already occurred; however, it can be used to alert the user to some issue.

PostMerge

post-merge hooks run after a git merge executes successfully with no merge conflicts. A hook failing in this case does not prevent the merge since it has already occurred; however, it can be used to alert the user to some issue.

PostRewrite

post-rewrite hooks run after a commit is modified by a git commit --amend or git rebase. A hook failing in this case does not prevent the rewrite since it has already occurred; however, it can be used to alert the user to some issue.

PreCommit

pre-commit hooks are run after git commit is executed, but before the commit message editor is displayed. If a hook fails, the commit will not be created. These hooks are ideal for syntax checkers, linters, and other checks that you want to run before you allow a commit to even be created.

WARNING: pre-commit hooks cannot have side effects

pre-commit hooks currently do not support hooks with side effects (such as modifying files and adding them to the index with git add). This is a consequence of Overcommit's pre-commit hook stashing behavior to ensure hooks are run against only the changes you are about to commit.

Without Overcommit, the proper way to write a pre-commit hook would be to extract the staged changes into temporary files and lint those files instead of whatever contents are in your working tree (as you don't want unstaged changes to taint your results). Overcommit takes care of this for you, but to do it in a generalized way introduces this limitation. See the thread tracking this issue for more details.

PrePush

pre-push hooks are run during git push, after remote refs have been updated but before any objects have been transferred. If a hook fails, the push is aborted.

PreRebase

pre-rebase hooks are run during git rebase, before any commits are rebased. If a hook fails, the rebase is aborted.

Repo-Specific hooks

Out of the box, overcommit comes with a set of hooks that enforce a variety of styles and lints. However, some hooks only make sense in the context of a specific repository.

For example, you can have a number of simple checks that run against your code to catch common errors. For example, if you use RSpec, you can make sure all spec files contain the line require 'spec_helper'.

Inside our repository, we can add the file .git-hooks/pre_commit/ensure_spec_helper.rb in order to automatically check our spec files:

module Overcommit::Hook::PreCommit
  class EnsureSpecHelper < Base
    def run
      errors = []

      applicable_files.each do |file|
        if File.read(file) !~ /^require 'spec_helper'/
          errors << "#{file}: missing `require 'spec_helper'`"
        end
      end

      return :fail, errors.join("\n") if errors.any?

      :pass
    end
  end
end

The corresponding configuration for this hook would look like:

PreCommit:
  EnsureSpecHelper:
    enabled: true
    description: 'Checking for missing inclusion of spec_helper'
    include: '**/*_spec.rb'

Adding Existing Git Hooks

You might already have hook scripts written which you'd like to integrate with Overcommit right away. To make this easy, Overcommit allows you to include your hook script in your configuration without writing any Ruby code. For example:

PostCheckout:
  CustomScript:
    enabled: true
    required_executable: './bin/custom-script'

So long as a command is given (either by specifying the command option directly or specifying required_executable) a special hook is created that executes the command and appends any arguments and standard input stream that would have been passed to the regular hook. The hook passes or fails based on the exit status of the command.

The script is executed as if Git were calling the hook directly. If you want to understand which arguments are passed to the script depending on the type of hook, see the git-hooks documentation.

Security

While Overcommit can make managing Git hooks easier and more convenient, this convenience can come at a cost of being less secure.

Since installing Overcommit hooks will allow arbitrary plugin code in your repository to be executed, you expose yourself to an attack where checking out code from a third party can result in malicious code being executed on your system.

As an example, consider the situation where you have an open source project. An attacker could submit a pull request which adds a post-checkout hook that executes some malicious code. When you fetch and checkout this pull request, the post-checkout hook will be run on your machine, along with the malicious code that you just checked out.

Overcommit attempts to address this problem by storing a signature of your configuration and all hook plugin code since the last time it ran. When the signature changes, a warning is displayed alerting you to which plugins have changed. It is then up to you to manually verify that the changes are not malicious, and then continue running the hooks.

The signature is derived from the contents of the plugin's source code itself and any configuration for the plugin. Thus a change to the plugin's source code or your local repo's .overcommit.yml file could result in a signature change.

Disabling Signature Checking

In typical usage, your plugins usually don't change too often, so this warning shouldn't become a nuisance. However, users who work within proprietary repositories where all developers who can push changes to the repository already have a minimum security clearance may wish to disable this check.

While not recommended, you can disable signature verification by setting verify_signatures to false in your .overcommit.yml file.

Regardless of whether you have verify_signatures disabled for your project, if you are running Overcommit for the first time you will need to sign your configuration with overcommit --sign. This needs to happen once so Overcommit can record in your local git repo's configuration (outside of source control) that you intend to enable/disable verification. This way if someone else changes verify_signatures you'll be asked to confirm the change.

Contributing

We love contributions to Overcommit, be they bug reports, feature ideas, or pull requests. See our guidelines for contributing to best ensure your thoughts, ideas, or code get merged.

Community

All major discussion surrounding Overcommit happens on the GitHub issues list.

Changelog

If you're interested in seeing the changes and bug fixes between each version of overcommit, read the Overcommit Changelog.

Author: SDS
Source Code: https://github.com/sds/overcommit 
License: MIT License

#git #hook 

Overcommit: A Fully Configurable and Extendable Git Hook Manager
React Tutorial

React Tutorial

1648524659

React useCallback Hook - What and How

A Guide to React useCallback Hook

The React useCallback hook can help you improve performance of your React apps. Learn about how React useCallback works and how to use it.

The React useCallback hook can help you improve performance of your React apps. It is weird that useCallback hook is one of the hooks that are not discussed as often. In this tutorial, you will learn about what React useCallback is, how it works and how to use it. You will also learn a bit about memoization.
 

Introduction to React useCallback hook

The main purpose of React useCallback hook is to memoize functions. The main reason for this is increasing performance of your React applications. How is this related? Every time your component re-renders it also re-creates functions that are defined inside it. Memoizing functions helps you prevent this.

When you memoize a function with useCallback hook that function is basically stored in cache. Quick example. Imagine that something causes your component to re-render. Let’s say there is a change of state. Usually, this re-render would, by default, also cause React to re-create all functions defined in your component.

This may not happen with useCallback hook and memoization. When you memoize a function React may not re-create that function just because the component re-rendered. Instead, React can skip the re-creation and return the memoized function. This can help you save resources and time and improve performance of your application.

The syntax of useCallback hook

If you already know the React useEffect hook you will find the syntax of useCallback familiar. They are actually almost the same. Similarly to useEffect hook, useCallback also accepts two parameters. The first parameter is the function you want to memoize. The second parameter is an array of dependencies.

This array of dependencies specifies values React should watch. When any of these values changes, React should re-create the function. Otherwise, it should return the memoized version of the function.

// Import useCallback hook from React:
import { useCallback } from 'react'

export default function App() {
  // Use useCallback to memoize function:
  const memoizedFunc = useCallback(() => {
    someFunction() // Function that will be memoized.
  }, [/* depOne, depTwo, ...dep */]) // <= Dependency array.

  // A bit shorter version:
  const memoizedFunc = useCallback(() => someFunction(), [])

  return (
    <div className="App">
      {/* Your component */}
    </div>
  )
}

The power of dependencies

The array of dependencies is important. It helps React understand when to return the memoized function and when to re-create it. Why re-create it? Wasn’t the purpose of memoization preventing this from happening? Well, yes and no. Yes, you want to prevent the function from being re-created.

However, if the function depends on some input, you want to re-create that function when the input changes. Otherwise, you would execute the function with old input that is no longer relevant. For example, let’s say you have a function that greets the user using their name.

This function will depend on the name of the current user. If you memoize it the first time you create it, it will remember the first name. When the name changes, it will not register it. It will greet every subsequent user using the first name. Solution for this is adding the name as a dependency.

When you specify the name as dependency, React will automatically re-create the function when the name changes. When new user arrives, and the name changes, the function will be re-created. It will update its input, use the latest value of name, and greet the user using a correct name.

A simple example

Let’s demonstrate the power of dependencies and memoization on a simple example. Imagine you have a simple component that contains input and button. The input allows the user to specify her name. This name will be stored in local state created with useState hook. Click on the button logs the name to the console.

The handler function for the button will be memoized with useCallback hook. On the first try, you forget to include the name as a dependency for the hook. What you do instead is specify the dependency array as an empty array. This tells React that it should create the function only on the initial render.

When something happens that causes a subsequent re-render of the component, it should return the memoized version of the function. Remember that changing state causes React to re-render. This helps keep everything in sync. What happens when the user write her name in the input and clicks the button?

The user will be probably surprised. The console will show the initial value of the “name” state. The reason is that when the function was created, the value of name was the initial value. When the name changed React didn’t re-create the function and the function didn’t know the name has changed.

// Note: this will not work as you may expect:
// Import useCallback and useState hooks from React.
import { useCallback, useState } from 'react'

export default function App() {
  // Create state for name:
  const [name, setName] = useState('')

  // Create and memoize function for logging name:
  const handleShowName = useCallback(() => {
    console.log(name)
  }, []) // <= Notice the empty array with dependencies.

  // Each click on the button will log
  // the initial value of "name" state, i.e. the ''.

  return (
    <div className="App">
      {/* Change "name" state when input changes: */}
      <input value={name} onChange={(event) => setName(event.target.value)} />

      {/* Attach handleShowName function */}
      <button onClick={handleShowName}>Show name</button>
    </div>
  )
}

A simple way to fix this is adding the “name” state as a dependency. Now, React will watch this value and re-create the function whenever the name changes. This will ensure that when the user changes the name the function will always have the latest information and log correct value.

// Note: this will not work as you may expect:
import { useCallback, useState } from 'react'

export default function App() {
  // Create state for name
  const [name, setName] = useState('')

  // Create and memoize function for logging name:
  const handleShowName = useCallback(() => {
    console.log(name)
  }, [name]) // <= Add "name" state as dependency.

  return (
    <div className="App">
      {/* Change name state when input changes: */}
      <input value={name} onChange={(event) => setName(event.target.value)} />

      {/* Attach handleShowName function */}
      <button onClick={handleShowName}>Show name</button>
    </div>
  )
}

Working with dependencies and when to re-create memoized function

The array of dependency, the second parameter, tells React when memoized function should be re-created. There are basically three options.

After every render

First, React can re-create the function after every render of your component. This pretty much defeats the whole purpose of useCallback hook, but it is still something you can do. For this, all you have to do is to omit the dependencies array. Use useCallback hook only with the function you want to memoize.

// Import useCallback hook from React:
import { useCallback } from 'react'

export default function App() {
  // Use useCallback to memoize function:
  const memoizedFunc = useCallback(() => someFunction())
  // Omit the dependency parameter (array).

  return (
    <div className="App">
      {/* Your component */}
    </div>
  )
}

If you really want to do this, you can simply skip using the useCallback hook. This option will lead to same result as declaring a function without the useCallback hook. The function will be re-created on every re-render and never memoized.

// Import useCallback hook from React:
import { useCallback } from 'react'

export default function App() {
  // Normal function:
  const someFunction = () => (/* Do something */)

  return (
    <div className="App">
      {/* Your component */}
    </div>
  )
}

Only after initial render

The second option is to create the function only after the initial render. When a subsequent re-render happens, React will return the memoized version of the function. This can be useful in two cases. First, when the function should always return the same result and probably doesn’t work with external input.

The second case is when the function works with external input(s), but that input doesn’t change. If the input doesn’t change or the function doesn’t depend on any external input, you may think about memoizing it. To do this, pass an empty array as the dependency parameter.

// Import useCallback hook from React:
import { useCallback } from 'react'

export default function App() {
  // Use useCallback to memoize function:
  const memoizedFunc = useCallback(() => someFunction(), [])
  // Pass an empty array as dependency parameter.

  return (
    <div className="App">
      {/* Your component */}
    </div>
  )
}

When specific value(s) change

The last option is to re-create the function when only specific value or values change. If some of the values change React will re-create the function to ensure it has the latest data. Otherwise, it will return the memoized version of the function. For this, specify the values you want to watch in the dependency array as a parameter.

From now, when any of these watched values change React will automatically re-create the function. Otherwise, it will return the memoized version. Remember that only one value you specified as a dependency has to change for React to re-create the function, not all of them.

// Import useCallback hook from React:
import { useCallback, useState } from 'react'

export default function App() {
  const [name, setName] = useState('')
  const [email, setEmail] = useState('')
  const [isValid, setIsValid] = useState(false)

  // Create and memoize form handler
  const handleFormSubmit = useCallback(
    () => {
      // Submit form.
    },
    [name, email, isValid], // <= Watch "name", "email" and "isValid".
  )

  return (
    <form className="App">
      {/* Your form component */}

      <button onClick={handleFormSubmit}></button>
    </form>
  )
}

A word of caution

Just because there is some tool doesn’t mean you have to use it. The same also applies to React useCallback hook. The purpose of this hook is to improve performance of heavy components. It is not intended to be a default “wrapper” for every single function you declare in your component.

So, don’t assume that you have to use useCallback every time you declare a function. You don’t. Use this hook in heavy components that use multiple functions and these functions don’t have to be re-created on every render. Even then, consider the potential gain and loses.

Will memoization help you measurably improve performance? Or, will it only introduce more complexity to your code, while any performance gains will be barely noticeable? For small and light components useCallback might not make a difference.

Conclusion

The React useCallback hook can be useful for improving performance of your apps, by storing your functions for later use, instead of re-creating them on every re-render. This can improve re-rendering behavior and performance of heavy components. I hope this tutorial helped you understand how useCallback hook works and how to use it.

Original article source at https://blog.alexdevero.com

#react #hook #reacthook #programming 

React useCallback Hook - What and How
Rocio  O'Keefe

Rocio O'Keefe

1647791940

Hooks: Advanced code sharing between widgets

Flutter Hooks

Hooks are a new kind of object that manage the life-cycle of a Widget. They exist for one reason: increase the code-sharing between widgets by removing duplicates.

Motivation

StatefulWidget suffers from a big problem: it is very difficult to reuse the logic of say initState or dispose. An obvious example is AnimationController:

class Example extends StatefulWidget {
  final Duration duration;

  const Example({Key key, required this.duration})
      : super(key: key);

  @override
  _ExampleState createState() => _ExampleState();
}

class _ExampleState extends State<Example> with SingleTickerProviderStateMixin {
  AnimationController? _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: widget.duration);
  }

  @override
  void didUpdateWidget(Example oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.duration != oldWidget.duration) {
      _controller!.duration = widget.duration;
    }
  }

  @override
  void dispose() {
    _controller!.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

All widgets that desire to use an AnimationController will have to reimplement almost all of this logic from scratch, which is of course undesired.

Dart mixins can partially solve this issue, but they suffer from other problems:

  • A given mixin can only be used once per class.
  • Mixins and the class share the same object.
    This means that if two mixins define a variable under the same name, the result may vary between compilation fails to unknown behavior.

This library proposes a third solution:

class Example extends HookWidget {
  const Example({Key key, required this.duration})
      : super(key: key);

  final Duration duration;

  @override
  Widget build(BuildContext context) {
    final controller = useAnimationController(duration: duration);
    return Container();
  }
}

This code is functionally equivalent to the previous example. It still disposes the AnimationController and still updates its duration when Example.duration changes. But you're probably thinking:

Where did all the logic go?

That logic has been moved into useAnimationController, a function included directly in this library (see Existing hooks) - It is what we call a Hook.

Hooks are a new kind of object with some specificities:

They can only be used in the build method of a widget that mix-in Hooks.

The same hook can be reused arbitrarily many times. The following code defines two independent AnimationController, and they are correctly preserved when the widget rebuild.

Widget build(BuildContext context) {
  final controller = useAnimationController();
  final controller2 = useAnimationController();
  return Container();
}

Hooks are entirely independent of each other and from the widget.
This means that they can easily be extracted into a package and published on pub for others to use.

Principle

Similar to State, hooks are stored in the Element of a Widget. However, instead of having one State, the Element stores a List<Hook>. Then in order to use a Hook, one must call Hook.use.

The hook returned by use is based on the number of times it has been called. The first call returns the first hook; the second call returns the second hook, the third call returns the third hook and so on.

If this idea is still unclear, a naive implementation of hooks could look as follows:

class HookElement extends Element {
  List<HookState> _hooks;
  int _hookIndex;

  T use<T>(Hook<T> hook) => _hooks[_hookIndex++].build(this);

  @override
  performRebuild() {
    _hookIndex = 0;
    super.performRebuild();
  }
}

For more explanation of how hooks are implemented, here's a great article about how is was done in React: https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e

Rules

Due to hooks being obtained from their index, some rules must be respected:

DO always prefix your hooks with use:

Widget build(BuildContext context) {
  // starts with `use`, good name
  useMyHook();
  // doesn't start with `use`, could confuse people into thinking that this isn't a hook
  myHook();
  // ....
}

DO call hooks unconditionally

Widget build(BuildContext context) {
  useMyHook();
  // ....
}

DON'T wrap use into a condition

Widget build(BuildContext context) {
  if (condition) {
    useMyHook();
  }
  // ....
}

About hot-reload

Since hooks are obtained from their index, one may think that hot-reloads while refactoring will break the application.

But worry not, a HookWidget overrides the default hot-reload behavior to work with hooks. Still, there are some situations in which the state of a Hook may be reset.

Consider the following list of hooks:

useA();
useB(0);
useC();

Then consider that we edited the parameter of HookB after performing a hot-reload:

useA();
useB(42);
useC();

Here everything works fine and all hooks maintain their state.

Now consider that we removed HookB. We now have:

useA();
useC();

In this situation, HookA maintains its state but HookC gets hard reset. This happens because, when a hot-reload is perfomed after refactoring, all hooks after the first line impacted are disposed of. So, since HookC was placed after HookB, it will be disposed.

How to create a hook

There are two ways to create a hook:

A function

Functions are by far the most common way to write hooks. Thanks to hooks being composable by nature, a function will be able to combine other hooks to create a more complex custom hook. By convention, these functions will be prefixed by use.

The following code defines a custom hook that creates a variable and logs its value to the console whenever the value changes:

ValueNotifier<T> useLoggedState<T>([T initialData]) {
  final result = useState<T>(initialData);
  useValueChanged(result.value, (_, __) {
    print(result.value);
  });
  return result;
}

A class

When a hook becomes too complex, it is possible to convert it into a class that extends Hook - which can then be used using Hook.use.
As a class, the hook will look very similar to a State class and have access to widget life-cycle and methods such as initHook, dispose and setState.

It is usually good practice to hide the class under a function as such:

The following code defines a hook that prints the total time a State has been alive on its dispose.

class _TimeAlive extends Hook<void> {
  const _TimeAlive();

  @override
  _TimeAliveState createState() => _TimeAliveState();
}

class _TimeAliveState extends HookState<void, _TimeAlive> {
  DateTime start;

  @override
  void initHook() {
    super.initHook();
    start = DateTime.now();
  }

  @override
  void build(BuildContext context) {}

  @override
  void dispose() {
    print(DateTime.now().difference(start));
    super.dispose();
  }
}
Result useMyHook() {
  return use(const _TimeAlive());
}

Existing hooks

Flutter_Hooks already comes with a list of reusable hooks which are divided into different kinds:

Primitives

A set of low-level hooks that interact with the different life-cycles of a widget

NameDescription
useEffectUseful for side-effects and optionally canceling them.
useStateCreates a variable and subscribes to it.
useMemoizedCaches the instance of a complex object.
useRefCreates an object that contains a single mutable property.
useCallbackCaches a function instance.
useContextObtains the BuildContext of the building HookWidget.
useValueChangedWatches a value and triggers a callback whenever its value changed.

Object-binding

This category of hooks the manipulation of existing Flutter/Dart objects with hooks. They will take care of creating/updating/disposing an object.

dart:async related hooks:

NameDescription
useStreamSubscribes to a Stream and returns its current state as an AsyncSnapshot.
useStreamControllerCreates a StreamController which will automatically be disposed.
useFutureSubscribes to a Future and returns its current state as an AsyncSnapshot.

Animation related hooks:

NameDescription
useSingleTickerProviderCreates a single usage TickerProvider.
useAnimationControllerCreates an AnimationController which will be automatically disposed.
useAnimationSubscribes to an Animation and returns its value.

Listenable related hooks:

NameDescription
useListenableSubscribes to a Listenable and marks the widget as needing build whenever the listener is called.
useValueNotifierCreates a ValueNotifier which will be automatically disposed.
useValueListenableSubscribes to a ValueListenable and return its value.

Misc hooks:

A series of hooks with no particular theme.

NameDescription
useReducerAn alternative to useState for more complex states.
usePreviousReturns the previous argument called to [usePrevious].
useTextEditingControllerCreates a TextEditingController.
useFocusNodeCreatex a FocusNode.
useTabControllerCreates and disposes a TabController.
useScrollControllerCreates and disposes a ScrollController.
usePageControllerCreates and disposes a PageController.
useAppLifecycleStateReturns the current AppLifecycleState and rebuilds the widget on change.
useOnAppLifecycleStateChangeListens to AppLifecycleState changes and triggers a callback on change.
useTransformationControllerCreates and disposes a TransformationController.
useIsMountedAn equivalent to State.mounted for hooks.

Contributions

Contributions are welcomed!

If you feel that a hook is missing, feel free to open a pull-request.

For a custom-hook to be merged, you will need to do the following:

Describe the use-case.

Open an issue explaining why we need this hook, how to use it, ... This is important as a hook will not get merged if the hook doesn't appeal to a large number of people.

If your hook is rejected, don't worry! A rejection doesn't mean that it won't be merged later in the future if more people show interest in it. In the mean-time, feel free to publish your hook as a package on https://pub.dev.

Write tests for your hook

A hook will not be merged unless fully tested to avoid inadvertendly breaking it in the future.

Add it to the README and write documentation for it.

A Flutter implementation of React hooks: https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889

Author: rrousselGit
Source Code: https://github.com/rrousselGit/flutter_hooks 
License: MIT License

#dart #flutter #hook 

Hooks: Advanced code sharing between widgets
Khaitan

Khaitan

1647420494

The Ultimate Guide to the React useReducer Hook

The React useReducer hook is very useful when you need to manage complex states. Learn about how useReducer hook works and how to use it.

The React useReducer hook is a very good alternative to useState when you need to manage complex states with multiple values. In this tutorial, you will learn about this React hook. You will learn about how useReducer hook works. You will also learn how to use it to manage state.
 

A quick introduction to React useReducer hook

The React useReducer hook is quite similar to useState hook. Like the useState hook, it also allows you to manage state of your React applications. The advantage of useReducer is that it makes it easier to work with complex states. By complex state I mean state with multiple sub-values, an object with key-value pairs.

The useReducer hook makes this easier by using more structural approach. That said, this doesn’t mean that you should useReducer hook only to deal with such states. You can useReducer just as well with simple states that contain single primitive value. The way useReducer hook works is simple.

It uses two pieces of data, state and reducer function. The reducer is a pure function that takes a state and an action. The actions tells the reducer what you want it to do. What is the update you want to do to the state. For example, increment number, decrement number, push new value to array, etc.

Reducer function takes these inputs, applies the action you specified, and returns a new state value. This new state value is an updated version of the state you provided it with. Something to remember. Reducer should not change the old one directly. About the syntax.

A note on pure functions

About “pure” functions. A function is pure when it follows two rules. First, the function always returns the same output if you pass in the same arguments. Second, the function does not produce any side-effects. This means that the function has no effect on its surroundings.

Put simply, the function doesn’t work with the outside world. It works only with inputs you passed into it. A simple example of pure function can be a function that takes two numbers as parameters and returns their sum. If you pass in the same numbers, you will get the same result. This confirms the first rule.

The function doesn’t do anything with the code outside it. It works solely with those two numbers it gets as input. This confirms the second rule. We can say that the function is pure. Now, let’s say that the function stores the result in an outside variable. In this case, the function is not pure because it breaks the second rule.

When the function has an effect on outside world it is not pure. Changing variables outside it is such an effect. It would also not be pure if it logged the result or some message. These logs are also side-effects and thus break the second rule.

The syntax of useReducer hook

The React useReducer hook accepts three parameters. The first two parameters are required. These two are the reducer and state. The reducer is the reducer function we discussed above. The state is any initial state value. This is the same initial state you know from working with useState hook.

Aside to these two, the useReducer hooks also accepts third, optional parameter. This parameter is initializer. This initializer allows you to initialize the state lazily with a function. The result returned by this function becomes the initial state value.

This can be useful when you want to create initial state, but it involves some expensive operation, to generate the initial data. Just remember that React will invoke the initializer function only after the initial render, not after subsequent re-renders. That said, you will probably not need it as often.

The useReducer hook will return two things, or values. First is the current state. The second is a dispatch function. This function allows you to update the state you passed to the useReducer hook.

// useReducer hook syntax:
const [state, dispatch] = useReducer(reducer, initialState, init)

Initial state

Before you can start using the useReducer hook you need two things, initial state and reducer function. Let’s start with the initial state. Initial state can be anything from primitive data type to object. Whatever fits your current situation. What you have to do is to create this state somewhere, and assign it to a variable.

// A simple initial state object:
const initialState = {
  name: '',
  email: '',
  role: '',
  isActive: false,
}

Reducer function

The second thing is the reducer function. The reducer function accepts two parameters: the state and action. It takes these two and updates the state, based on the action dispatched. It is very common to create the structure of reducer function, and handle each action, with switch statement.

The main reason is that switch is usually more readable than if...else statement. Especially when you work with multiple actions. That said, if you prefer if...else statement go ahead and use that. About the structure. The reducer has to have a case, or if block, for each action you want to use to update the state.

Each of these actions should do two things. First, it should copy the current state. Reducer is a pure function. It is not supposed to change the existing state. What it does instead is it creates copies of it and works with them. It is common to create copies of old state by spreading the old, using spread.

The second thing reducer will do for each case, or block, is updating specific state value with the new value. Put together, it will basically copy the old state and overwrite only the values that should be updated. After that, it will return the new state. Aside to this there should be also a default case or else block.

This case or block can do two things. First, it can return the original state unchanged. Second, it can throw an error about non-existing action. Similarly to initial state, you define the reducer as a function somewhere in your code. Don’t pass it to the reducer as a whole.

// Create reducer function:
const reducer = (state, action) => {
  // Create switch to handle all actions:
  switch (action.type) {
    case 'SET_NAME':
      // Handle 'SET_NAME' action:
      return {
        ...state, // Copy the old state.
        name: action.payload // Update relevant value.
      }
    case 'SET_EMAIL':
      // Handle 'SET_EMAIL' action:
      return {
        ...state, // Copy the old state.
        email: action.payload // Update relevant value.
      }
    case 'SET_ROLE':
      // Handle 'SET_ROLE' action:
      return {
        ...state, // Copy the old state.
        role: action.payload // Update relevant value.
      }
    case 'SET_IS_ACTIVE':
      // Handle 'SET_IS_ACTIVE' action:
      return {
        ...state, // Copy the old state.
        isActive: action.payload // Update relevant value.
      }
    default:
      // Throw an error when none of cases matches the action.
      throw new Error('Unexpected action')
  }
}

Action, type and payload

In the reducer function example you could see action.type and action.payload. This is because when you update the state with dispatch function returned by the useReducer hook you pass in an object. This object contains two keys, type and payload. The type tell reducer function what action you want to make.

Reducer function then uses this information, the type, to use one of the switch cases, or if blocks. The payload is where you put the new value for the state. These two names are not mandatory. They are just a common practice among React developers. You can use any names you want. Just make sure to use correct names in your reducer.

// Dispatched object example to set name:
dispatch({
  type: 'SET_NAME',
  payload: 'Victor'
})

// Dispatched object example to set role:
dispatch({
  type: 'SET_ROLE',
  payload: 'Admin'
})

// Dispatched object example to set isActive:
dispatch({
  type: 'SET_IS_ACTIVE',
  payload: true
})

Putting it all together

You have the initial state and reducer function. Now, you can use them with the useReducer hook and let the hook handle state management for you. The process is simple. Call the useReducer hook and pass in the reducer function and initial state, in this order. This will return the state and dispatch function.

When you want to update specific state value you use the dispatch function. You call this function passing an object as an argument. This is the action. This object will contain two keys, type and payload (or any names you chose). The type must match one of the switch cases in your reducer function.

The value of payload is the value you want to update the state with. It is the new value you want to store in the state. The state value returned by the useReducer hook will always give you the latest values of the state. This is just like when you use useState hook. In this case, the state is still the same. The state updater function is the dispatch.

// Import useReducer hook from React:
import { useReducer } from 'react'

// Create initial state:
const initialState = {
  name: '',
  email: '',
  role: '',
  isActive: false,
}

// Create reducer function:
const reducer = (state, action) => {
  // Create switch to handle all actions:
  switch (action.type) {
    case 'SET_NAME':
      // Handle 'SET_NAME' action:
      return {
        ...state, // Copy the old state.
        name: action.payload // Update relevant value.
      }
    case 'SET_EMAIL':
      // Handle 'SET_EMAIL' action:
      return {
        ...state, // Copy the old state.
        email: action.payload // Update relevant value.
      }
    case 'SET_ROLE':
      // Handle 'SET_ROLE' action:
      return {
        ...state, // Copy the old state.
        role: action.payload // Update relevant value.
      }
    case 'SET_IS_ACTIVE':
      // Handle 'SET_IS_ACTIVE' action:
      return {
        ...state, // Copy the old state.
        isActive: action.payload // Update relevant value.
      }
    default:
      // Throw an error when none of cases matches the action.
      throw new Error('Unexpected action')
  }
}

// Create simple component:
export default function App() {
  // Call useReducer hook, passing in
  // previously created reducer function
  // and initial state:
  const [state, dispatch] = useReducer(reducer, initialState)

  return (
    <div className="App">
      {/*
        Create input for "name" and use dispatch
        to update "name" state value on input change.
      */}
      <input
        type="text"
        name="name"
        value={state.name}
        onChange={(event) => dispatch({
          type: 'SET_NAME', // Dispatch 'SET_NAME' action.
          payload: event.target.value // Set input value as payload.
        })}
      />

      {/*
        Create input for "email" and use dispatch
        to update "email" state value on input change.
      */}
      <input
        type="email"
        name="email"
        value={state.email}
        onChange={(event) => dispatch({
          type: 'SET_EMAIL', // Dispatch 'SET_EMAIL' action.
          payload: event.target.value // Set input value as payload.
        })}
      />

      {/*
        Create select for selecting "role" and use dispatch
        to update "role" state value on select change.
      */}
      <select
        onChange={(event) => dispatch({
          type: 'SET_ROLE', // Dispatch 'SET_ROLE' action.
          payload: event.target.value // Set input value as payload.
        })}
      >
        <option value="" selected></option>
        <option value="Admin">Admin</option>
        <option value="User">User</option>
        <option value="guest">Guest</option>
      </select>

      {/*
        Create checkbox for isActive and use dispatch
        to update "isActive" state value on checkbox change.
      */}
      <label>
        <input
          type="checkbox"
          checked={state.isActive}
          onChange={(event, checked) => dispatch({
            type: 'SET_IS_ACTIVE', // Dispatch 'SET_IS_ACTIVE' action.
            payload: checked // Set checkbox checked value as payload.
          })}
        />
        Is active?
      </label>
    </div>
  )
}

Conclusion

The React useReducer hook is a good alternative to useState hook. Where useReducer can be very useful is when you have to deal with complex states. In these situations, useReducer might be a better choice than useState. I hope that this tutorial helped you understand how the React useReducer hook works and how to use it.

Original article source at https://blog.alexdevero.com

#react #programming #javascript #hook 

The Ultimate Guide to the React useReducer Hook

Hook-std: Hook and Modify Stdout/stderr

hook-std

Hook and modify stdout and stderr

Install

npm install hook-std

Usage

import assert from 'node:assert';
import {hookStdout} from 'hook-std';

const promise = hookStdout(output => {
	promise.unhook();
	assert.strictEqual(output.trim(), 'unicorn');
});

console.log('unicorn');
await promise;

You can also unhook using the second transform method parameter:

import assert from 'node:assert';
import {hookStdout} from 'hook-std';

const promise = hookStdout((output, unhook) => {
	unhook();
	assert.strictEqual(output.trim(), 'unicorn');
});

console.log('unicorn');
await promise;

API

hookStd(options?, transform)

Hook streams in streams option, or stdout and stderr if none are specified.

Returns a Promise with a unhook() method which, when called, unhooks both stdout and stderr and resolves the Promise with an empty result.

hookStdout(options?, transform)

Hook stdout.

Returns a Promise with a unhook() method which, when called, unhooks stdout and resolves the Promise with an empty result.

hookStderr(options?, transform)

Hook stderr.

Returns a Promise with a unhook() method which, when called, unhooks stderr and resolves the Promise with an empty result.

options

Type: object

silent

Type: boolean
Default: true

Suppress stdout/stderr output.

once

Type: boolean
Default: false

Automatically unhook after the first call.

streams

Type: stream.Writable[]
Default: [process.stdout, process.stderr]

The writable streams to hook. This can be useful for libraries allowing users to configure a writable stream to write to.

transform

Type: Function

Receives stdout/stderr as the first argument and the unhook method as the second argument. Return a string to modify it. Optionally, when in silent mode, you may return a boolean to influence the return value of .write(…).

Author: Sindresorhus
Source Code: https://github.com/sindresorhus/hook-std 
License: MIT License

#node #hook 

Hook-std: Hook and Modify Stdout/stderr
React Tutorial

React Tutorial

1645775776

How to Build Your Own React Custom Hooks

React offers a couple of built-in hooks. You can also create your React custom hooks. Learn what custom hooks are and how to create them.

React offers a number of built-in hooks you can use right away. Aside to these, you can also create your own custom hooks. In this tutorial, you will learn what React custom hooks are and how to create your own. You will also learn what rules you have to follow when creating custom hooks.
 

A quick overview of React hooks

It was in React v16.8 when React hooks were introduced by React team. Since then, hooks quickly rose in popularity among React developers, and even beyond. Until then, when you wanted to use state and lifecycle methods inside React components you had to use JavaScript classes.

React hooks changed this paradigm. With hooks, you no longer have to create components with classes just so you can use state. You can just as well create functional components. Then, you can use hooks to “enhance” these components with whatever feature you need, be it a state, lifecycle method or something else.

The word “hook” may sound a bit vague. To make it easier, think about hooks simply as functions. This is what hooks are, plain JavaScript functions. These functions allow you “hook into” various React features, such as state and lifecycle. Most popular examples are useState and useEffect hooks.

The useState hooks allows you to bring state to function components. With useEffect hook, you can work with component lifecycle. There is a couple of React hooks ready to use. However, these hooks can’t do everything. They can’t cover every possible scenario. Fortunately, these hooks are not the only option.

An introduction to React custom hooks

Aside to releasing a number of official React hooks, React team made it also possible to create React custom hooks. So, if you can’t find a hook that would solve some problem you have, you can create your own. You can also use React custom hooks to make your code that involves stateful logic reusable.

As we already discussed, hooks are basically plain JavaScript functions. One difference is that hooks are used for a specific purpose. Another is that React hooks allow you to use other React hooks, such as useState, and even other custom hooks. So, don’t worry that creating custom hooks will be difficult.

Creating custom hooks will be similar to writing regular JavaScript functions. You will probably get a grasp on it quickly. It will be even faster if you know how to use hooks such as useState and useEffect because you are likely to use these hooks in your custom hooks. But before we get into that, there are some rules to learn.

Rules of hooks

Before you create your first custom hook, there are two rules you should know. These rules are called Rules of hooks. First, you can call hooks only at the top level. Never call hooks inside nested functions, conditions or loops. If you want to use conditions, or loops, use them inside the hook, not the other way around.

The second rule is that you should call hooks only from React function components or other hooks. There is also one practice for creating custom hooks. Always start the name of the hook with “use” prefix. This is more like a good rule of thumb than a rule. It is good to follow to make code more readable, but it is not required.

Building your own React custom hooks

React custom hooks are JavaScript functions. This means few things. First, when you create a custom hook you are writing a function. Second, function name should start with “use”. Remember, this is a good rule of thumb for creating custom hooks. Third, you can use other React hooks inside your custom hooks.

These are the things to remember. To make this more hands-on, let’s put this together and create few examples of React custom hooks. This can make it easier to understand how custom hooks work and how to create them.

Example no.1: useWindowSize hook

The first example will be a hook that will return current window size. First, the name. The name should be descriptive and start with “use”. Something like “useWindowSize” sounds like a good candidate. Second, the logic of the hook. When you call this hook, it will do few things.

The first thing it will do is getting the current window size and returning it. Second, it will attach event listener to window object and listen to resize event. When this event happens, the hook will detect the new window size and return it again. This will repeat every time the resize event happens.

Custom hooks can use other React hooks. This means that we can use useState hook to store the latest window dimension in a state and return the value of this state. We can also use useEffect hook to attach the event listener for resize event. We can use this useEffect hook to remove the event listener.

We can do this by returning a clean up function. This function will call the removeEventListener method, passing the resize event and function for handling the resize.

// Import useEffect and useState hooks from React:
import { useEffect, useState } from 'react'

// Create custom useWindowSize hook function:
export function useWindowSize() {
  // Create function to get current window size:
  const getWindowSize = () => ({
    innerHeight: window.innerHeight,
    innerWidth: window.innerWidth,
    outerHeight: window.outerHeight,
    outerWidth: window.outerWidth,
  })

  // Create state for window size data:
  const [windowSize, setWindowSize] = useState(getWindowSize())
  // It also uses the getWindowSize() to set the initial state.

  // Create handler function for resize event:
  function handleResize() {
    // Update state value:
    setWindowSize(getWindowSize())
  }

  // Create a side-effect
  useEffect(() => {
    // Attach event listener for "resize" event:
    window.addEventListener('resize', handleResize)

    return () => {
      // Remove event listener for "resize" event:
      window.removeEventListener('resize', handleResize)
    }
  }, [])

  // Return current window size:
  return windowSize
}

When you want to use this hook, import it in your React component and call it. Remember to assign that call to variable so that you can get the window size every time it changes.

// Import the useWindowSize hook:
import { useWindowSize } from './hook'

export default function App() {
  // Call the useWindowSize hook and assign it to variable:
  const windowSize = useWindowSize()

  // Display the current window size:
  return (
    <div>
      <ul>
        <li>window inner width: {windowSize.innerWidth}</li>
        <li>window inner height: {windowSize.innerHeight}</li>
        <li>window outer width: {windowSize.outerWidth}</li>
        <li>window outer height: {windowSize.outerHeight}</li>
      </ul>
    </div>
  )
}

Example no.2: useToggle hook

Another simple, but useful hook can be hook for managing toggle state. Such a hook could be useful for creating collapsible components for example. It could help you check for current toggle state and switching between “on” and “off” state. It could also allow to reset the state or set it manually.

This hook will be simple. It will use useState hook to store toggle state. Aside to this, it will have two functions, handleReset and handleToggle. The handleReset will reset the toggle state to the initial value. The handleToggle will reverse current toggle state. It switch from “on” to “off” and the other way around.

The value we will return from this hook will be an object. This object will contain all these methods and current value of the toggle state. We will also return the setter method for state to allow setting custom state. When you import this hook, you will be able to import anything inside the object it returns.

// Import useEffect and useState hooks from React:
import { useState } from 'react'

// Create custom useToggle hook function:
export function useToggle(initialState = false) {
  // Create toggle state:
  const [toggle, setToggle] = useState(initialState)

  // Create handler function for resetting the state:
  const handleReset = () => setToggle(initialState)

  // Create handler function for toggling the state:
  const handleToggle = () => setToggle(prevState => !prevState)

  // Return the state, state setter function and handler functions:
  return {
    on: toggle,
    set: setToggle,
    reset: handleReset,
    toggle: handleToggle
  }
}

Just like with the previous hook, you can now import this useToggle in your React component. When you call it, you can use destructuring assignment to assign anything from the object this hook returns to a variable so you can use it.

// Import the useToggle hook:
import { useToggle } from './hook'

export default function App() {
  // Call the useToggle hook and assign variables,
  // using destructuring assignment:
  const { on, set, reset, toggle } = useToggle()

  // Use any method or state returned from the hook:
  return (
    <div>
      <p>On: {on ? 'true' : 'false'}</p>

      <button onClick={() => set(true)}>Set to on</button>
      <button onClick={reset}>Reset</button>
      <button onClick={toggle}>Toggle</button>
    </div>
  )
}

Example no.3: useLocalStorage hook

Third and last example. It became popular to store application or website data in local or session storage. This hook will accept two parameters: name of the key to store and initial value for this key. When called, this hook will first check if local storage is available in the browser.

If local storage is not available, it will return the initial value passed as argument. If local storage is available, it will check if any key with the same name exists. If it does, it will retrieve its data. Otherwise, it will return the initial value. Whatever is returned will become the state of the hook.

This all will happen during the initial load. It will happen inside initializer function for useState hook. The second part of the hook will be a handler function for storing data in local storage. This function will accept one parameter, the data to store. It will first take this value and store it inside the hook state.

Then, it will store the value in local storage. The name of the key for this data will be the name of the key passed to the hook during the call. The last part, returning something. This hook will return two things: current value of the state, data loaded from local storage, and handler function for storing data in local storage.

// Import useState hook from 'react':
import { useState } from 'react'

export function useLocalStorage(keyName, initialValue) {
  // Create state for local storage:
  const [storedValue, setStoredValue] = useState(() => {
    try {
      // Check if local storage is available:
      if (typeof window === 'undefined') {
        return initialValue
      }

      // Check if item with the same name exists in local storage:
      const item = window.localStorage.getItem(keyName)

      // Return parsed data from storage or return the initialValue:
      return item !== null ? JSON.parse(item) : initialValue;
    } catch (error) {
      // Catch any errors and log them:
      console.log(error)

      // Return the initialValue:
      return initialValue
    }
  })

  // Create handler function for storing value in local storage:
  const setValue = (value) => {
    try {
      // Store the value in the state:
      setStoredValue(value)

      // Store the value in local storage:
      window.localStorage.setItem(keyName, JSON.stringify(value))
    } catch (error) {
      // Catch any errors and log them:
      console.log(error)
    }
  }

  // Return latest data and handler function for storing new data:
  return [storedValue, setValue]
}

The way to use this hook will be similar to using useState. When you call it, you pass in the name of the key and data for that key. The hook will return array with the data and handler function for storing new data. The data returned will be either the initial value or any data that is already stored in local storage for that key.

// Import the useLocalStorage hook:
import { useLocalStorage } from './hook'

export default function App() {
  // Call the useLocalStorage hook and assign variables,
  // again, using destructuring assignment:
  const [value, setValue] = useLocalStorage('name', 'Joe')

  // Store data typed in the input in local storage
  // and also display them in the DOM:
  return (
    <div>
      <p>{value}</p>

      <input type="text" onChange={(e) => setValue(e.currentTarget.value)} />
    </div>
  )
}

Conclusion

Official React hooks are very useful tools for every React developer. However, these official hooks can’t do everything you may want or need. Writing your own React custom hooks can help you solve this problem. I hope that this tutorial helped you learn what React custom hooks are, how they work and how to create your own.

Original article source at https://blog.alexdevero.com

#react #hook #javascript 

How to Build Your Own React Custom Hooks
React Tutorial

React Tutorial

1645516184

How to Create New Contexts and Work with Them in React

Getting Started with React useContext Hook and React Context

The React useContext hook allows to work with React context from anywhere in your app. Learn how to create new contexts and work with them.

React context makes it easy to create globally accessible data, and states. The useContext hook allows you to work with React contexts from anywhere and pass its data throughout your app. This tutorial will show you how to create new context, how to get values from it and how to change them.

React context, global states, prop drilling

When you work with data they are usually one of two types, global or local. Global can be accessed from anywhere. Local only from the place where they are defined, and down the tree. This also applies to states. You can have global states and you can have local states. Which one is the best choice depends on situation.

React context API makes it easy to create these global states. That said, there is one problem with these global states. They are often difficult to use in nested components. It can take a lot of prop drilling to get the data from the top to where you need them. You may have to pass these data through multiple components.

One way to solve this is making those data local. However, this would lead to duplicate code. It would also go against the idea of having one source of truth that is globally accessible. Another solution is to skip all prop drilling and simply reach to the context from the component where you need those data.

This is the goal of the React useContext hook. The React useContext hook promises to help you with two things. First, to help you reach out to any context and from anywhere. Second, to work with values exposed through this context. This includes both, getting those values as well as changing them. Let’s take a look at how it works.

The context

Using React context requires getting done few things. First, you have to create a context. You achieve this by using createContext() method shipped with React. This context will be the global state available for use across the app. Well, at least one them because your React app can contain infinite number of contexts.

// context.jsx

// Import createContext() method from React:
import { createContext } from 'react'

// Create new context:
export const newContext = createContext()

Notice that we are declaring the context as empty, basically assigning it undefined. Don’t worry. This doesn’t mean this context will be empty forever. It will be empty just for now when you create it. Later, in the next step, you will add values to it. Also notice that we are exporting the context.

The reason for this is simple. The useContext hook accepts a context as a parameter. So, if we want to use the useContext hook to access the context anywhere in the app the context itself must be also accessible anywhere. This means we must export it from where it is.

The context provider

The second thing you have to do is to create a provider for your new context. This provider is a component that provides your app with the value(s) stored inside the context. Provider wraps all components that should be able to access the context. This is important to remember.

Components will be able to communicate with provider only if they are provider’s children. It doesn’t matter where in the component tree they are. What matters is that the provider is used as a wrapper somewhere in the tree above. In general, provider is used as a wrapper for the entire app.

This guarantees that any component in the app will be able to communicate with the provider. If you have multiple providers, you can wrap one inside another while keeping the app as the last child. This will ensure the app has access to all providers up the tree. Now, let’s create the provider.

Creating the context provider

Creating the provider is similar to creating a regular React component. Nowadays, provider is usually created as a function component. You give this component some name. It is a good practice to end the name with “Provider”. It makes it easier to understand the code when you read it.

Inside this component, you can use any react hook you want. For example, you can use useState hook to create new state for the provider. You can then expose this state by setting it as a value for the provider. This will make it available for any component wrapped with the provider.

You can also use useCallback hook to create memoized functions. These functions can work with the state, update its values. You can also expose these functions by setting them as values for the provider. Again, this will make them available for components wrapped with the provider.

The most important part is where the rendering happens, what follows the return statement. Here, you will use the context for the first time. The context you’ve previously created also contains a provider component your new provider will render. You can access this provider component using object dot notation (newContext.Provider).

Since we want to use this provider as a wrapper, it should render any children it wraps.

// context.jsx

// Import createContext() method from React:
import { createContext } from 'react'

// Create new context:
export const newContext = createContext()

// Create new provider component:
export const NewProvider = (props) => {
  return (
    {/* Render Provider provided by previously created context: */}
    <newContext.Provider>
      {/* Render Provider's children: */}
      {props.children}
    </newContext.Provider>
  )
}

Make sure to also export your new Provider component so you can use it where you need it. The next step is to take the Provider and use it as a wrapper for the components for which you want to make the data provided by this provider accessible. You can also use it to wrap the main app component.

This will make anything exposed by the provider accessible to any component in the app.

// index.jsx

// Import React and React-dom:
import { StrictMode } from 'react'
import ReactDOM from 'react-dom'

// Import the NewProvider component:
import { NewProvider } from './context'

// Import app component:
import App from './App'

// Create the main component:
const rootElement = document.getElementById('root')
ReactDOM.render(
  <StrictMode>
    {/* Use the NewProvider to wrap the whole app: */}
    <NewProvider>
      {/* The app component rendering all other components: */}
      <App />
    </NewProvider>
  </StrictMode>,
  rootElement
)

Adding state to the context provider

The provider itself is useless if it doesn’t provide any value, or values, to the app. In order to fix this, you need two things. First, you need some value, some data, you want to be available through the provider. Second, you have to make this data accessible from the provider.

The first can be fixed by creating new local state inside the provider. The useState hook will be perfect for this. The value of this state will be what you want to share across the app. Since useState hook also creates an update function, this will also give you a way to update this shared state.

To fix the second thing, you have to add value attribute to the myContext.Provider component returned by the NewProvider component. The value of this attribute can be anything from a primitive data type to an object. If you want to share a single value, the first will be sufficient.

If you want to share multiple values, or values and functions, it will be better to use an object. It is nice to make values available across the app. Even better is to also allow changing these values across the app. So, let’s go with the object. Let’s create new state and expose both, the state and its update function via the provider.

// context.jsx

// Import createContext() method and useState hook from React:
import { createContext, useState } from 'react'

// Create new context:
export const newContext = createContext()

// Create new provider component:
export const NewProvider = (props) => {
  // Create local state:
  const [state, setState] = useState('')

  // Prepare values to share:
  const val = {
    state, // The state itself
    setState // The state update function
  }

  return (
    {/* Set "val" as the value for "value" attribute: */}
    <newContext.Provider value={value}>
      {props.children}
    </newContext.Provider>
  )
}

Accessing context with the useContext hook

You are almost done. You have context, you have provider and you have something to share via the provider. You have also wrapped the app with the provider and exposed some value via the Provider’s value attribute. You can now access the state and setState function exposed via the provider anywhere in the app.

To achieve this, you need just two things. The first thing is the React useContext hook. The second thing is the exported context, the one you created in the beginning with the createContext() method. When you combine these two you will have immediate access to state and setState you created in NewProvider component.

Let’s create the main App component. You saw this component in the index.jsx file as the direct child of the provider (Creating the context provider section). This component will be simple. It will contain two components: heading showing welcome message and current value of state and input to update the state via setState.

You will get both, state and setState, from the newContext context. Remember that this context is provided by the NewProvider component. You will get those values by calling the React useContext hook and passing the newContext context as an argument.

// Import useContext hook from React:
import { useContext } from 'react'

// Import newContext context:
import { newContext } from './context'

// Create the App component:
export default function App() {
  // Access the state and setState values in newContext:
  const { state, setState } = useContext(newContext)

  return (
    <div>
      {/* Display the value of "state" */}
      <h1>Hello {state}</h1>

      <h2>Change name:</h2>
      {/*
        Use "setState" update function to update the current value
        of "state" with the current value of input:
      */}
      <input type="text" onChange={(e) => setState(e.target.value)} />
    </div>
  )
}

Multiple contexts

There is basically no limit to how many contexts, and providers, you can have in your React app. You can have as many as you want, as long as you remember to add each provider as a wrapper. For example, we can add additional context for email to this simple sample app. This will require new context and new Provider component.

First, let’s create new context for email. This will be almost a mirror copy of the context you already have. You will mostly change just the names.

// email-context.jsx

// Import createContext() method from React:
import { createContext, useState } from 'react'

// Create new context:
export const emailContext = createContext()

// Create new email provider component:
export const EmailProvider = (props) => {
  // Create local state for email:
  const [email, setEmail] = useState('')

  // Prepare values for sharing:
  const val = {
    email,
    setEmail,
  }

  // Render emailContext.Provider exposing "val" variable:
  return (
    <emailContext.Provider value={val}>
      {/* Render children components: */}
      {props.children}
    </emailContext.Provider>
  )
}

Next, you have to import the email context in the main file, where you render the App to the root element. When you have multiple providers their order doesn’t really matter. Important thing that the app, or some component where you want to use data from those providers, is wrapped with those providers.

import { StrictMode } from 'react'
import ReactDOM from 'react-dom'

import { NewProvider } from './context'

// Import new email provider:
import { EmailProvider } from './email-context'

import App from './App'

const rootElement = document.getElementById('root')
ReactDOM.render(
  <StrictMode>
    {/* Add email provider as another wrapper of the App component: */}
    <EmailProvider>
      <NewProvider>
        <App />
      </NewProvider>
    </EmailProvider>
  </StrictMode>,
  rootElement
)

With that, you can now use the React useContext hook with emailContext to access the email and setEmail anywhere in the app.

import { useContext } from 'react'

import { newContext } from './context'

// Import new email context:
import { emailContext } from './email-context'

export default function App() {
  const { state, setState } = useContext(newContext)

  // Access the email and setEmail values in emailContext:
  const { email, setEmail } = useContext(emailContext)

  return (
    <div>
      {/* Render the value of "email": */}
      <h1>
        Hello {state}, {email}
      </h1>

      <h2>Change name:</h2>
      <input type="text" onChange={(e) => setState(e.target.value)} />

      <h2>Change email:</h2>
      {/*
        Allow to to update the current value of "email"
        via the "setEmail" update function and text input:
      */}
      <input type="text" onChange={(e) => setEmail(e.target.value)} />
    </div>
  )
}

Conclusion

Creating global states with React context is very easy. With the help of React useContext hook it is also easy to access these contexts and their data. I hope that this tutorial helped you understand how to create contexts and their providers and how to use useContext hook to communicate with them.

Original article source at https://blog.alexdevero.com

#react #hook #javascript 

How to Create New Contexts and Work with Them in React
Thierry  Perret

Thierry Perret

1644899922

Learn React Hooks – Guide du débutant

Les composants fonctionnels n'ont pas toujours été la méthode préférée pour déclarer des composants dans React.

Avant l'introduction de la version 16.8 de React, les composants fonctionnels étaient traités comme des citoyens de seconde classe. Ils ne pouvaient pas gérer l'état, la logique et de nombreuses autres fonctionnalités de React, et nous ne les utilisions que pour rendre des composants très simples à l'interface utilisateur.

La version 16.8 de React a résolu ces problèmes en introduisant React Hooks, qui permet aux développeurs d'utiliser ces fonctionnalités de réaction dans des composants fonctionnels.

Dans cet article, vous apprendrez :

  • Que sont les crochets React
  • Quatre crochets React courants avec des exemples de la façon de les écrire dans vos applications
  • Enfin, nous verrons comment écrire vos propres React Hooks personnalisés

Que sont les React Hooks ?

Les crochets sont des fonctions React intégrées introduites dans React version 16.8. Ils vous permettent d'utiliser des fonctionnalités de la bibliothèque React telles que les méthodes de cycle de vie, l'état et le contexte dans des composants fonctionnels sans avoir à vous soucier de la réécrire dans une classe.

Chaque nom React Hook est précédé du mot "use". Par exemple, useStateou useEffect. Ce format a été choisi car les Hooks permettent aux développeurs d'utiliser les fonctionnalités spéciales de la bibliothèque React. Vous utilisez donc usecette fonctionnalité spéciale de la bibliothèque React.

Pourquoi utiliser React Hooks ?

De nombreux développeurs sont sceptiques quant à l'apprentissage de React Hooks. Mais vous ne devriez pas l'être. Voici quelques raisons pour lesquelles vous devriez commencer à utiliser React Hooks :

Les classes dans React peuvent être assez déroutantes

Les cours sont un obstacle à l'apprentissage de React correctement. Pour les utiliser, vous devez comprendre le fonctionnement du mot- thisclé. Vous devez également vous rappeler constamment de lier les gestionnaires d'événements, ainsi que d'autres méthodes redondantes rencontrées lorsque vous travaillez avec des classes dans React.

Les composants des classes sont complexes et peuvent être difficiles à comprendre

Les composants de classe sont généralement volumineux et essaient d'effectuer de nombreuses opérations. À la longue, ils deviennent difficiles à comprendre.

Les crochets résolvent ce problème en vous permettant de séparer les grands composants en diverses fonctions plus petites, plutôt que d'avoir à forcer toute la logique en un seul composant.

Les crochets ont des composants plus courts et une meilleure lisibilité

Les composants de classe sont livrés avec beaucoup de code passe-partout. Considérez le composant de compteur ci-dessous :

class Counter extends Component {
    constructor(props) {
        super(props)
        this.state = {
        	count: 1,
        }
    }
    render() {
        return (
            <div>
                The Current Count: {this.state.count}
                <div>
                <button onClick={this.setState({ count: this.state.count - 1 })}>
                add
                </button>
                <button onClick={this.setState({ count: this.state.count + 1 })}>
                subtract
                </button>
                </div>
            </div>
    );
    }
}

Voici un code équivalent utilisant un composant fonctionnel et React Hooks :

function Counter  ()  {
    const [count, setCount] = useState(1);
    return (
        <div>
            The Current Count: {this.state.count}
            <div>
                <button onClick={() => setCount(count + 1)}>add</button>
                <button onClick={() => setCount(count - 1)}>subtract</button>
            </div>
        </div>
    );
};

Remarquez comment le composant de classe est beaucoup plus complexe. Vous avez besoin d'une classe pour étendre React, d'un constructeur pour initialiser l'état, et vous devez référencer le mot- thisclé partout.

L'utilisation de composants fonctionnels supprime une grande partie de cela, de sorte que notre code devient plus court et plus facile à lire et à entretenir.

Règles d'utilisation des crochets React

Lors de l'utilisation de React Hooks, il y a quelques règles à respecter :

  • N'appelez les crochets qu'au niveau supérieur d'un composant : vous ne devez pas utiliser les crochets à l'intérieur des boucles, des conditions ou des fonctions imbriquées. Au lieu de cela, utilisez toujours Hooks au niveau supérieur de votre fonction React, avant tout mot-clé de retour.
  • N'appelez que des crochets à partir de fonctions React : n'appelez jamais de crochets à partir de fonctions JavaScript normales. Vous pouvez :
    ✅ Appeler des Hooks à partir des composants fonctionnels de React.
    ✅ Appelez des crochets à partir de crochets personnalisés.

Crochets de réaction les plus courants

À ce jour, React a 10 crochets intégrés. Regardons les quatre plus courants :

  • useState
  • useEffect
  • useContext
  • useReducer

crochet useState

Le crochet useState vous permet de créer, mettre à jour et manipuler l'état à l'intérieur des composants fonctionnels.

React a ce concept d'état, qui sont des variables qui contiennent des données dont dépendent nos composants et qui peuvent changer avec le temps. Chaque fois que ces variables changent, React met à jour l'interface utilisateur en restituant le composant dans le DOM avec les valeurs actuelles des variables d'état.

Le crochet prend un seul argument optionnel : une valeur initiale pour l'état. Ensuite, il renvoie un tableau de deux valeurs :

  • La variable d'état
  • Une fonction pour mettre à jour l'état

Prenons l'exemple d'un composant de compteur :

Pour utiliser un Hook, la première étape consiste à importer le Hook en haut du fichier :

import { useState } from "react";

Ensuite, initialisez le Hook avec une valeur. En raison du fait qu'il renvoie un tableau, vous pouvez utiliser la déstructuration du tableau pour accéder aux éléments individuels du tableau, comme ceci :

const [count, setCount] = useState(0);

Avec cela, le code du composant sera :

import { useState } from "react";

function Counter() {
    // Declare a new state variable, which we'll call "count"
    const [count, setCount] = useState(0);
    return (
        <div>
        Current Cart Count: {count}
            <div>
            <button onClick={() => setCount(count - 1)}>Add to cart</button>
            <button onClick={() => setCount(count + 1)}>Remove from cart</button>
            </div>
        </div>
    );
}

Voici à quoi ressemblera le composant une fois rendu.

Compteur

En cliquant sur le bouton Ajouter au panier ou Supprimer du panier , la valeur du nombre de variables d'état changera et le composant sera restitué avec la valeur mise à jour de l'état.

useEffect Hook

Si vous connaissez les méthodes de cycle de vie de la classe React, vous pouvez considérer le useEffectcrochet comme les méthodes de cycle de vie componentDidMount, componentDidUpdateet componentWillUnmount, toutes combinées en une seule fonction. Il vous permet de répliquer les méthodes de cycle de vie de React dans des composants fonctionnels.

Le useEffectcrochet vous permet d'effectuer des effets secondaires dans les composants de fonction. Les effets secondaires sont des actions qui peuvent s'exécuter parallèlement aux opérations principales d'un composant, telles que les interactions d'API externes, la modification de variables d'état et la récupération de données.

Le useEffecthook accepte 2 arguments :

  • Une fonction avec le code à exécuter
  • Un tableau qui contient une liste de valeurs de la portée du composant (variables d'accessoires, de contexte et d'état), connu sous le nom de tableau de dépendances, qui indique au crochet de s'exécuter chaque fois que sa valeur est mise à jour. S'il n'est pas fourni, le crochet s'exécutera après chaque rendu.

Voici un exemple d'utilisation du Hook :

import { useState, useEffect } from "react";
function Counter() {
    // Declare state variables
    const [count, setCount] = useState(0);
    const [product, setProduct] = useState("Eggs");
    useEffect(() => {
    	console.log(`${product} will rule the world!`);
    });
    return (
        <div>
        Current {product}'s count: {count}
            <div>
                <button onClick={() => setCount(count + 1)}>Add to cart</button>
                <button onClick={() => setCount(count - 1)}>Remove from cart</button>
                Change Product:{" "}
                <input type="text" onChange={(e) => setProduct(e.target.value)} />
            </div>
        </div>
    );
}

Dans l'exemple, l'effet s'exécutera après chaque mise à jour d'état.

Effet par défaut

wAAAAAAAAIdjI8JkO231psw0iuv3rz7D4biSJbmiabqyrbuVgAAOw ==

Comment déclencher conditionnellement un effet

Pour exécuter le crochet uniquement lorsque certaines valeurs ont changé, transmettez les variables en tant que dépendance dans le tableau :

useEffect(() => {
	console.log(`${product} will rule the world!`);
}, [product]); // Only re-run the effect if the value of product changes

Avec ce changement, le crochet ne s'exécutera que lors du premier rendu et lorsque la valeur du produit est modifiée.

Effect-dependency-array

Comment exécuter une fois sur le premier rendu

Si vous voulez qu'un effet ne s'exécute qu'une seule fois lors du premier rendu, comme faire des appels d'API lorsque le composant est rendu pour la première fois, vous pouvez passer un tableau vide comme dépendance comme ceci :

useEffect(() => {
	console.log("This runs once on first render");
}, []);

En fournissant un tableau vide, il indique au crochet d'écouter les changements d'état zéro, de sorte qu'il ne s'exécutera qu'une seule fois.

useContext Hook

Le useContextHook fonctionne avec l'API React Context. Il vous permet de rendre des données particulières accessibles à tous les composants de l'application, quelle que soit leur profondeur d'imbrication.

React a un flux de données unidirectionnel, où les données ne peuvent être transmises que du parent à l'enfant. Pour transmettre des données (comme l'état) d'un parent à un composant enfant, vous devrez les transmettre manuellement en tant qu'accessoire à différents niveaux en fonction de la profondeur d'imbrication du composant enfant.

Pour des données telles que la langue préférée de l'utilisateur, le thème ou les propriétés de l'utilisateur authentifié, il est fastidieux de devoir les transmettre manuellement dans l'arborescence des composants.

L'API Context de React et le useContextHook facilitent la transmission de données entre tous les composants de l'application.

Il accepte un objet de contexte créé à l'aide de React.createContextet renvoie le contexte actuel comme suit :

const value = useContext(SomeContext);

Regardons un exemple du fonctionnement du Hook :

Tout d'abord, créez un contexte pour utiliser le Hook. Par exemple, voici un UserContext pour obtenir la valeur des utilisateurs actuels :

import React from "react";
// some mock context values
const users = [
{
    name: "Harry Potter",
    occupation: "Wizard",
},
{
    name: "Kent Clark",
    occupation: "Super hero",
},
];

export const UserContext = React.createContext(users);

Chaque contexte a un wrapper de fournisseur, qui permet à ses composants enfants de s'abonner aux modifications du contexte et transmet la valeur du contexte via une prop de valeur.

Si la prop de valeur du fournisseur est mise à jour, ses composants enfants consommateurs seront restitués avec la nouvelle valeur de contexte.

function Users() {
return (
    <UserContext.Provider value={users}>
    <UserProfile />
    </UserContext.Provider>
);
}

Dans l'exemple, UserProfileon fait de la composante consommatrice du contexte.

import React, { useContext } from "react";
import { UserContext } from "./App";

export function UserProfile() {
    const users = useContext(UserContext);
    return (
        <div>
            {users.map((user) => (
            <li>
            I am {user.name} and I am a {user.occupation}!
            </li>
            ))}
        </div>
    );
}

Cela affichera les propriétés des utilisateurs actuels :

zrquu592NphAAAAAElFTkSuQmCC

useReducer Hook

Le useReducerCrochet est une alternative au useStateCrochet. La différence est qu'il permet une logique plus complexe et des mises à jour d'état qui impliquent plusieurs sous-valeurs.

Semblable à useState, useReducervous permet de créer des variables de type état qui entraînent la mise à jour de l'interface utilisateur chaque fois qu'elles changent.

Ce Hook accepte 2 arguments : une fonction réductrice et un état initial.

useReducer(reducer, initialState);

Il renvoie un tableau de deux valeurs qui peuvent être déstructurées à la valeur actuelle de l'état et une fonction de répartition.

const [state, dispatch] = useReducer(reducer, initialState);

Découvrons ses arguments et les valeurs renvoyées :

  • state : Il s'agit de la valeur actuelle de initialState passée au Hook.
  • reducer : Le reducer est une fonction qui accepte l'état et une action. Sur la base de ces arguments, il détermine comment la valeur de l'état va changer.
  • dispatch : La fonction dispatch est la façon dont nous passons une action à la fonction reducer. Il distribue l'action à utiliser pour mettre à jour l'état.

En règle générale, nous parcourons le type d'actions que nous avons effectuées dans notre application via une instruction switch pour déterminer comment la valeur de l'état va changer. C'est ainsi que le Hook met à jour les valeurs de son état.

function reducer(state, action) {
    switch (action.type) {
        case "CASE_1":
        return {
        	updatedState,
        };
        case "CASE_2":
        return {
        	updatedState,
        };
        default:
        	return state;
    }
}

La fonction dispatch distribue généralement un objet au format :

dispatch({ type: "ACTION_TYPE", payload: optionalArguments });

Où type est la description de l'action et la charge utile est les arguments que vous souhaitez transmettre au réducteur.

Comment créer des crochets personnalisés

Un crochet personnalisé est l'idée d'extraire la logique de composant couramment utilisée de l'interface utilisateur dans des fonctions JavaScript en utilisant les crochets React déjà disponibles. Cela vous aide à éviter la duplication de code et vous permet de rendre cette logique réutilisable dans plusieurs composants.

Examinons un exemple de crochet personnalisé qui renverra une réponse à partir de toute URL d'API valide que nous lui transmettrons.

//useFetch.js
import { useState, useEffect } from "react";

export function useFetch(url) {
	//values
    const [data, setData] = useState(null);
    const [error, setError] = useState("");
    useEffect(() => {
        fetch(url)
        .then(res => {
            if (!res.ok) {
            throw Error("something wrong, çould not connect to resource");
        }
        setData(res.json());
        })
        .then(() => {
        	setError("");
        })
        .catch( error => {
            console.warn(`sorry an error occurred, due to ${error.message} `);
            setData(null);
            setError(error.message);
        });
    }, [url]);
    return [data, error];
}

Vous pouvez désormais utiliser cette logique n'importe où dans votre application simplement en important la fonction et en passant un chemin d'API comme argument, plutôt que de tout écrire à partir de zéro.

Emballer

J'espère que vous avez pu voir à quel point React Hooks est utile. Ils vous permettent de créer des composants efficaces à la volée sans vous soucier des tracas liés aux composants de classe.

Qu'il s'agisse de vous permettre de vous concentrer sur l'écriture de votre code principal ou de vous permettre de créer vos propres crochets personnalisés... Les crochets React sont tellement cool ! Je suis ravi que vous les essayiez par vous-même.

Si vous avez trouvé cet article utile, partagez-le avec vos amis et votre réseau. Aussi, n'hésitez pas à vous connecter avec moi sur Twitter et mon blog où je partage un large éventail d'articles et de ressources pédagogiques gratuits.

Merci d'avoir lu et bon codage !

Link: https://www.freecodecamp.org/news/the-beginners-guide-to-react-hooks/

#react #hook #hooks 

Learn React Hooks – Guide du débutant
React Tutorial

React Tutorial

1643085923

Strategies to Tame useEffect

Strategies to Tame useEffect

It's easier to write code that makes the useEffect hook go off the rails than it is to write code that uses it the right way. Let's develop some strategies that you can use to make sure the useEffects you write always work when and how you expect them to work.

0:00 Introduction
0:27 Setup
1:38 Creating useFetch
6:42 [ DANGER ZONE ] Depending On Objects
7:30 All About References
13:17 Fixing Our Dependencies
15:16[ DANGER ZONE ] Function Dependencies
19:02 Quick Check-In 
19:50 Cleanup Functions
21:25 Depending On State Mutated In The useEffect
24:51 Outroduction

Code: https://github.com/jherr/taming-useeffect 

#react #hook

Strategies to Tame useEffect