当前位置: 首页 > 软件库 > 大数据 > 数据查询 >

graphql-hooks

🎣 Minimal hooks-first GraphQL client
授权协议 View license
开发语言 Java
所属分类 大数据、 数据查询
软件类型 开源软件
地区 不详
投 递 者 周子平
操作系统 跨平台
开源组织
适用人群 未知
 软件概览

graphql-hooks

Join the community on Spectrumci

�� Minimal hooks-first GraphQL client.

Features

  • �� First-class hooks API
  • ⚖️ Tiny bundle: only 7.6kB (2.8 gzipped)
  • �� Full SSR support: see graphql-hooks-ssr
  • �� Plugin Caching: see graphql-hooks-memcache
  • �� No more render props hell
  • Handle loading and error states with ease

Install

npm install graphql-hooks

or

yarn add graphql-hooks

Support

Consider polyfilling:

Quick Start

First you'll need to create a client and wrap your app with the provider:

import { GraphQLClient, ClientContext } from 'graphql-hooks'

const client = new GraphQLClient({
  url: '/graphql'
})

function App() {
  return (
    <ClientContext.Provider value={client}>
      {/* children */}
    </ClientContext.Provider>
  )
}

Now in your child components you can make use of useQuery

import { useQuery } from 'graphql-hooks'

const HOMEPAGE_QUERY = `query HomePage($limit: Int) {
  users(limit: $limit) {
    id
    name
  }
}`

function MyComponent() {
  const { loading, error, data } = useQuery(HOMEPAGE_QUERY, {
    variables: {
      limit: 10
    }
  })

  if (loading) return 'Loading...'
  if (error) return 'Something Bad Happened'

  return (
    <ul>
      {data.users.map(({ id, name }) => (
        <li key={id}>{name}</li>
      ))}
    </ul>
  )
}

Why graphql-hooks?

The first thing you may ask when seeing graphql-hooks is "Why not use Apollo hooks?".It's the comparison most will make. In fact, there's an article comparing the two over on LogRocket.

We believe graphql-hooks is a great choice as a hooks-first GraphQL client due to its concise API and package size.

In terms of performance, this is more of a grey area as we have no official benchmarks yet.

If you need a client that offers middleware and advanced cache configuration, then apollo-hooks may work out to be a good choice for your project if bundle size is not an issue.

Pros Cons
Small in size Middleware support
Concise API Less "advanced" caching configuration
Quick to get up and running

Table of Contents

API

GraphQLClient

Usage:

import { GraphQLClient } from 'graphql-hooks'
const client = new GraphQLClient(config)

config: Object containing configuration properties

  • url (Required): The url to your GraphQL server
  • ssrMode: Boolean - set to true when using on the server for server-side rendering; defaults to false
  • useGETForQueries: Boolean - set to true to use HTTP GET method for all queries; defaults to false. See HTTP Get Support for more info
  • subscriptionClient: An instance of SubscriptionClient from subscriptions-transport-ws or Client from graphql-ws
  • cache (Required if ssrMode is true, otherwise optional): Object with the following methods:
    • cache.get(key)
    • cache.set(key, data)
    • cache.delete(key)
    • cache.clear()
    • cache.keys()
    • getInitialState()
    • See graphql-hooks-memcache as a reference implementation
  • fetch(url, options): Fetch implementation - defaults to the global fetch API. Check Request interceptors for more details how to manage fetch.
  • FormData: FormData implementation - defaults to the global FormData API. Polyfill this in a node.js environment. See file-uploads-nodejs for more info.
  • fetchOptions: See MDN for info on what options can be passed
  • headers: Object, e.g. { 'My-Header': 'hello' }
  • logErrors: Boolean - defaults to true
  • onError({ operation, result }): Custom error handler
    • operation: Object with query, variables and operationName
    • result: Object containing data and error object that contains fetchError, httpError and graphqlErrors

client methods

  • client.setHeader(key, value): Updates client.headers adding the new header to the existing headers
  • client.setHeaders(headers): Replaces client.headers
  • client.removeHeader(key): Updates client.headers removing the header if it exists
  • client.logErrorResult({ operation, result }): Default error logger; useful if you'd like to use it inside your custom onError handler
  • request(operation, options): Make a request to your GraphQL server; returning a Promise
    • operation: Object with query, variables and operationName
  • options.fetchOptionsOverrides: Object containing additional fetch options to be added to the default ones passed to new GraphQLClient(config)

ClientContext

ClientContext is the result of React.createContext() - meaning it can be used directly with React's new context API:

Example:

import { ClientContext } from 'graphql-hooks'

function App() {
  return (
    <ClientContext.Provider value={client}>
      {/* children can now consume the client context */}
    </ClientContext.Provider>
  )
}

To access the GraphQLClient instance, call React.useContext(ClientContext):

import React, { useContext } from 'react'
import { ClientContext } from 'graphql-hooks'

function MyComponent() {
  const client = useContext(ClientContext)
}

useQuery

Usage:

const state = useQuery(query, [options])

Example:

import { useQuery } from 'graphql-hooks'

function MyComponent() {
  const { loading, error, data } = useQuery(query)

  if (loading) return 'Loading...'
  if (error) return 'Something bad happened'

  return <div>{data.thing}</div>
}

This is a custom hook that takes care of fetching your query and storing the result in the cache. It won't refetch the query unless query or options.variables changes.

  • query: Your GraphQL query as a plain string
  • options: Object with the following optional properties
    • variables: Object e.g. { limit: 10 }
    • operationName: If your query has multiple operations, pass the name of the operation you wish to execute.
    • persisted: Boolean - defaults to false; Pass true if your graphql server supports persisted flag to serve persisted queries.
    • useCache: Boolean - defaults to true; cache the query result
    • skip: Boolean - defaults to false; do not execute the query if set to true
    • skipCache: Boolean - defaults to false; If true it will by-pass the cache and fetch, but the result will then be cached for subsequent calls. Note the refetch function will do this automatically
    • ssr: Boolean - defaults to true. Set to false if you wish to skip this query during SSR
    • fetchOptionsOverrides: Object - Specific overrides for this query. See MDN for info on what options can be passed
    • updateData(previousData, data): Function - Custom handler for merging previous & new query results; return value will replace data in useQuery return value
      • previousData: Previous GraphQL query or updateData result
      • data: New GraphQL query result
    • client: GraphQLClient - If a GraphQLClient is explicitly passed as an option, then it will be used instead of the client from the ClientContext.

useQuery return value

const { loading, error, data, refetch, cacheHit } = useQuery(QUERY)
  • loading: Boolean - true if the query is in flight
  • data: Object - the result of your GraphQL query
  • refetch(options): Function - useful when refetching the same query after a mutation; NOTE this presets skipCache=true & will bypass the options.updateData function that was passed into useQuery. You can pass a new updateData into refetch if necessary.
    • options: Object - options that will be merged into the options that were passed into useQuery (see above).
  • cacheHit: Boolean - true if the query result came from the cache, useful for debugging
  • error: Object - Set if at least one of the following errors has occurred and contains:
    • fetchError: Object - Set if an error occurred during the fetch call
    • httpError: Object - Set if an error response was returned from the server
    • graphQLErrors: Array - Populated if any errors occurred whilst resolving the query

useManualQuery

Use this when you don't want a query to automatically be fetched, or wish to call a query programmatically.

Usage:

const [queryFn, state] = useManualQuery(query, [options])

Example:

import { useManualQuery } from 'graphql-hooks'

function MyComponent(props) {
  const [fetchUser, { loading, error, data }] = useManualQuery(GET_USER_QUERY, {
    variables: { id: props.userId }
  })

  return (
    <div>
      <button onClick={fetchUser}>Get User!</button>
      {error && <div>Failed to fetch user<div>}
      {loading && <div>Loading...</div>}
      {data && <div>Hello ${data.user.name}</div>}
    </div>
  )
}

If you don't know certain options when declaring the useManualQuery you can also pass the same options to the query function itself when calling it:

import { useManualQuery } from 'graphql-hooks'

function MyComponent(props) {
  const [fetchUser] = useManualQuery(GET_USER_QUERY)

  const fetchUserThenSomething = async () => {
    const user = await fetchUser({
      variables: { id: props.userId }
    })
    return somethingElse()
  }

  return (
    <div>
      <button onClick={fetchUserThenSomething}>Get User!</button>
    </div>
  )
}

useMutation

Mutations unlike Queries are not cached.

Usage:

const [mutationFn, state, resetFn] = useMutation(mutation, [options])

Example:

import { useMutation } from 'graphql-hooks'

const UPDATE_USER_MUTATION = `mutation UpdateUser(id: String!, name: String!) {
  updateUser(id: $id, name: $name) {
    name
  }
}`

function MyComponent({ id, name }) {
  const [updateUser] = useMutation(UPDATE_USER_MUTATION)
  const [newName, setNewName] = useState(name)

  return (
    <div>
      <input
        type="text"
        value={newName}
        onChange={e => setNewName(e.target.value)}
      />
      <button
        onClick={() => updateUser({ variables: { id, name: newName } })}
      />
    </div>
  )
}

The options object that can be passed either to useMutation(mutation, options) or mutationFn(options) can be set with the following properties:

  • variables: Object e.g. { limit: 10 }
  • operationName: If your query has multiple operations, pass the name of the operation you wish to execute.
  • fetchOptionsOverrides: Object - Specific overrides for this query. See MDN for info on what options can be passed
  • client: GraphQLClient - If a GraphQLClient is explicitly passed as an option, then it will be used instead of the client from the ClientContext.

In addition, there is an option to reset the current state before calling the mutation again, by calling resetFn(desiredState) where desiredState is optional and if passed, it will override the initial state with:

  • data: Object - the data
  • error: Error - the error
  • loading: Boolean - true if it is still loading
  • cacheHit: Boolean - true if the result was cached

useSubscription

To use subscription you can use either subscriptions-transport-ws or graphql-ws

API

useSubscription(operation, callback)

  • operation: Object - The GraphQL operation the following properties:
    • query: String (required) - the GraphQL query
    • variables: Object (optional) - Any variables the query might need
    • operationName: String (optional) - If your query has multiple operations, you can choose which operation you want to call.
    • client: GraphQLClient - If a GraphQLClient is explicitly passed as an option, then it will be used instead of the client from the ClientContext.
  • callback: Function - This will be invoked when the subscription receives an event from your GraphQL server - it will receive an object with the typical GraphQL response of { data: <your result>, errors?: [Error] }

Usage

First follow the quick start guide to create the client and povider. Then we need to update the config for our GraphQLClient passing in the subscriptionClient:

import { GraphQLClient } from 'graphql-hooks'
import { SubscriptionClient } from 'subscriptions-transport-ws'
// or
import { createClient } from 'graphql-ws'

const client = new GraphQLClient({
  url: 'http://localhost:8000/graphql',
  subscriptionClient: new SubscriptionClient('ws://localhost:8000/graphql', {
    /* additional config options */
  }),
  // or
  subscriptionClient: createClient({
    url: 'ws://localhost:8000/graphql'
    /* additional config options */
  })
})

Next, within our React app, we can now make use of the useSubscription hook.

import React, { useState } from 'react'
import { useSubscription } from 'graphql-hooks'

const TOTAL_COUNT_SUBSCRIPTION = `
  subscription TotalCount {
    totalCount {
      count
    }
  }
`

function TotalCountComponent() {
  const [count, setCount] = useState(0)
  const [error, setError] = useState(null)

  useSubscription({ query: TOTAL_COUNT_SUBSCRIPTION }, ({ data, errors }) => {
    if (errors && errors.length > 0) {
      // handle your errors
      setError(errors[0])
      return
    }

    // all good, handle the gql result
    setCount(data.totalCount.count)
  })

  if (error) {
    return <span>An error occurred {error.message}</span>
  }

  return <div>Current count: {count}</div>
}

Working Example:

See our subscription example which has both the client and server code to integrate subscriptions into your application.

Guides

SSR

See graphql-hooks-ssr for an in depth guide.

Pagination

GraphQL Pagination can be implemented in various ways and it's down to the consumer to decide how to deal with the resulting data from paginated queries. Take the following query as an example of offset pagination:

export const allPostsQuery = `
  query allPosts($first: Int!, $skip: Int!) {
    allPosts(first: $first, skip: $skip) {
      id
      title
      url
    }
    _allPostsMeta {
      count
    }
  }
`

In this query, the $first variable is used to limit the number of posts that are returned and the $skip variable is used to determine the offset at which to start. We can use these variables to break up large payloads into smaller chunks, or "pages". We could then choose to display these chunks as distinct pages to the user, or use an infinite loading approach and append each new chunk to the existing list of posts.

Separate pages

Here is an example where we display the paginated queries on separate pages:

import { React, useState } from 'react'
import { useQuery } from 'graphql-hooks'

export default function PostList() {
  // set a default offset of 0 to load the first page
  const [skipCount, setSkipCount] = useState(0)

  const { loading, error, data } = useQuery(allPostsQuery, {
    variables: { skip: skipCount, first: 10 }
  })

  if (error) return <div>There was an error!</div>
  if (loading && !data) return <div>Loading</div>

  const { allPosts, _allPostsMeta } = data
  const areMorePosts = allPosts.length < _allPostsMeta.count

  return (
    <section>
      <ul>
        {allPosts.map(post => (
          <li key={post.id}>
            <a href={post.url}>{post.title}</a>
          </li>
        ))}
      </ul>
      <button
        // reduce the offset by 10 to fetch the previous page
        onClick={() => setSkipCount(skipCount - 10)}
        disabled={skipCount === 0}
      >
        Previous page
      </button>
      <button
        // increase the offset by 10 to fetch the next page
        onClick={() => setSkipCount(skipCount + 10)}
        disabled={!areMorePosts}
      >
        Next page
      </button>
    </section>
  )
}

Infinite loading

Here is an example where we append each paginated query to the bottom of the current list:

import { React, useState } from 'react'
import { useQuery } from 'graphql-hooks'

// use options.updateData to append the new page of posts to our current list of posts
const updateData = (prevData, data) => ({
  ...data,
  allPosts: [...prevData.allPosts, ...data.allPosts]
})

export default function PostList() {
  const [skipCount, setSkipCount] = useState(0)

  const { loading, error, data } = useQuery(allPostsQuery, {
    variables: { skip: skipCount, first: 10 },
    updateData
  })

  if (error) return <div>There was an error!</div>
  if (loading && !data) return <div>Loading</div>

  const { allPosts, _allPostsMeta } = data
  const areMorePosts = allPosts.length < _allPostsMeta.count

  return (
    <section>
      <ul>
        {allPosts.map(post => (
          <li key={post.id}>
            <a href={post.url}>{post.title}</a>
          </li>
        ))}
      </ul>
      {areMorePosts && (
        <button
          // set the offset to the current number of posts to fetch the next page
          onClick={() => setSkipCount(allPosts.length)}
        >
          Show more
        </button>
      )}
    </section>
  )
}

File uploads

graphql-hooks complies with the GraphQL multipart request spec, allowing files to be used as query or mutation arguments. The same spec is also supported by popular GraphQL servers, including Apollo Server (see list of supported servers here).

If there are files to upload, the request's body will be a FormData instance conforming to the GraphQL multipart request spec.

import React, { useRef } from 'react'
import { useMutation } from 'graphql-hooks'

const uploadPostPictureMutation = `
  mutation UploadPostPicture($picture: Upload!) {
    uploadPostPicture(picture: $picture) {
      id
      pictureUrl
    }
  }
`

export default function PostForm() {
  // File input is always uncontrolled in React.
  // See: https://reactjs.org/docs/uncontrolled-components.html#the-file-input-tag.
  const fileInputRef = useRef(null)

  const [uploadPostPicture] = useMutation(uploadPostPictureMutation)

  const handleSubmit = event => {
    event.preventDefault()

    uploadPostPicture({
      variables: {
        picture: fileInputRef.current.files[0]
      }
    })
  }

  return (
    <form onSubmit={handleSubmit}>
      <input accept="image/*" ref={fileInputRef} type="file" />
      <button>Upload</button>
    </form>
  )
}

File uploads Node.js

const client = new GraphQLClient({
  url: 'https://domain.com/graphql',
  fetch: require('node-fetch'),
  FormData: require('formdata-node')
})

const uploadPostPictureMutation = `
  mutation UploadPostPicture($picture: Upload!) {
    uploadPostPicture(picture: $picture) {
      id
      pictureUrl
    }
  }
`

const { data, error } = await client.request({
  query: uploadPostPictureMutation,
  variables: { picture: createReadStream('some-file.txt') }
})

HTTP Get support

Using GET for queries can be useful, especially when implementing any sort of HTTP caching strategy. There are two ways you can do this:

Per Query

const { loading, error, data } = useQuery(MY_QUERY, {
  fetchOptionsOverrides: { method: 'GET' }
})

// same goes for useManualQuery
const [fetchSomething] = useManualQuery(MY_QUERY, {
  fetchOptionsOverrides: { method: 'GET' }
})

For All Queries

When you create your client, set the useGETForQueries option as true:

const client = new GraphQLClient({
  url: '/graphql',
  useGETForQueries: true
})

Authentication

You can have access the to the graphql-hooks client context by using the React's new context API. ClientContext is actually the result of React.createContext().

Login example

import React, { useState, useContext } from 'react'
import { useMutation, ClientContext } from 'graphql-hooks'

const LOGIN_MUTATION = `mutation LoginUser (name: String!, password: String!) {
  loginUser(name: $name, password: $password) {
    token
  }
}`

const Login = () => {
  const client = useContext(ClientContext)
  const [loginUserMutation] = useMutation(LOGIN_MUTATION)
  const [userName, setUserName] = useState()
  const [password, setPassword] = useState()

  const handleLogin = async e => {
    e.preventDefault()
    const { data, error } = await loginUserMutation({
      variables: { userName, password }
    })
    if (error) {
      // your code to handle login error
    } else {
      const { token } = data.loginUser
      client.setHeader('Authorization', `Bearer ${token}`)
      // your code to handle token in browser and login redirection
    }
  }
  return (
    <form onSubmit={handleLogin}>
      User Name:{' '}
      <input
        type={'text'}
        value={userName}
        onChange={e => setUserName(e.target.value)}
      />
      PassWord: <input
        type={'password'}
        value={password}
        onChange={e => setPassword(e.target.value)}
      />
      <input type={'submit'} value={'Login'} />
    </form>
  )
}

export default Login

In the above example we use useContext() hook to get access to the graphql-hooks clientContext.Then we request the token from the server by performing the loginUser mutation.In the case the login is success we set the token to the client's header (client.setHeader), otherwise we need to handle the error.For more information about graphql-hooks clientContext refer to GraphQLClient section.

Fragments

Coming soon!

Migrating from Apollo

For a real life example, compare the next.js with-apollo vs with-graphql-hooks. We have feature parity and the main-*.js bundle is a whopping 93% smaller (7.9KB vs 116KB).

ApolloClient ➡️ GraphQLClient

- import { ApolloClient } from 'apollo-client'
- import { InMemoryCache } from 'apollo-cache-inmemory'
+ import { GraphQLClient } from 'graphql-hooks'
+ import memCache from 'graphql-hooks-memcache'

- const client = new ApolloClient({
-  uri: '/graphql',
-  cache: new InMemoryCache()
- })
+ const client = new GraphQLClient({
+   url: '/graphql',
+   cache: memCache()
+ })

A lot of the options you'd pass to ApolloClient are the same as GraphQLClient:

  • uri ➡️ url
  • fetchOptions
  • onError - the function signature is slightly different
  • headers
  • fetch
  • cache

ApolloProvider ➡️ ClientContext.Provider

- import { ApolloProvider } from 'react-apollo'
+ import { ClientContext } from 'graphql-hooks'

function App({ client }) {
  return (
-    <ApolloProvider client={client}>
+    <ClientContext.Provider value={client}>
       {/* children */}
+    </ClientContext.Provider>
-    </ApolloProvider>
  )
}

Query Component ➡️ useQuery

- import { Query } from 'react-apollo'
- import gql from 'graphql-tag'
+ import { useQuery } from 'graphql-hooks'

function MyComponent() {
+ const { loading, error, data } = useQuery('...')

-  return (
-    <Query query={gql`...`}>
-     {({ loading, error, data}) => {
        if (loading) return 'Loading...'
        if (error) return 'Error :('

        return <div>{data}</div>
-      }}
-    </Query>
-  )
}

Query Component Props

A lot of options can be carried over as-is, or have direct replacements:

  • query ➡️ useQuery(query): Remove any usage of gql and pass your queries as strings.
  • variables ➡️ useQuery(query, { variables })
  • ssr ➡️ useQuery(query, { ssr })
  • Fetch Policies: See #75 for more info
    • cache-first: This the default behaviour of graphql-hooks
    • cache-and-network: The refetch function provides this behaviour it will set loading: true, but the old data will be still set until the fetch resolves.
    • network-only ➡️ useQuery(QUERY, { skipCache: true })
    • cache-only: Not supported
    • no-cache ➡️ useQuery(QUERY, { useCache: false })

Not yet supported

  • errorPolicy: Any error will set the error to be truthy. See useQuery for more details.
  • pollInterval
  • notifyOnNetworkStatusChange
  • skip
  • onCompleted: Similar ability if using useManualQuery
  • onError: Similar ability if using useManualQuery
  • partialRefetch

Query Component Render Props

- <Query query={gql`...`}>
-  {(props) => {}}
- </Query>
+ const state = useQuery(`...`)
  • props.loading ➡️ const { loading } = useQuery('...')
  • props.error ➡️ const { error } = useQuery('...'): The error value from useQuery is Boolean the details of the error can be found in either:
    • state.fetchError
    • state.httpError
    • state.graphQLErrors
  • props.refetch ➡️ const { refetch } = useQuery('...')
  • props.updateData(prevResult, options) ➡️ state.updateData(prevResult, newResult)

Not yet supported

  • props.networkStatus
  • props.startPolling
  • props.stopPolling
  • props.subscribeToMore

Mutation Component ➡️ useMutation

- import { Mutation } from 'react-apollo'
- import gql from 'graphql-tag'
+ import { useMutation } from 'graphql-hooks'

function MyComponent() {
+ const [mutateFn, { loading, error, data }] = useMutation('...')

-  return (
-    <Mutation mutation={gql`...`}>
-     {(mutateFn, { loading, error }) => {
        if (error) return 'Error :('

        return <button disabled={loading} onClick={() => mutateFn()}>Submit</button>
-      }}
-    </Mutation>
-  )
}

Mutation Props

  • mutation ➡️ useMutation(mutation) - no need to wrap it in gql
  • variables ➡️ useMutation(mutation, { variables }) or mutateFn({ variables })
  • ignoreResults ➡️ ️️️ const [mutateFn] = useMutation(mutation)
  • onCompleted ➡️ mutateFn().then(onCompleted)
  • onError ➡️ mutateFn().then(({ error }) => {...})

Not yet supported

  • update: Coming soon #52
  • optimisticResponse
  • refetchQueries
  • awaitRefetchQueries
  • context

Mutation Component Render Props

- <Mutation mutation={gql`...`}>
-  {(mutateFn, props) => {}}
- </Mutation>
+ const [mutateFn, state] = useMutation(`...`)
  • props.data ➡️ const [mutateFn, { data }] = useMutation()
  • props.loading ➡️ const [mutateFn, { loading }] = useMutation()
  • props.error ➡️ const [mutateFn, { error }] = useMutation(): The the details of the error can be found in either:
    • state.fetchError
    • state.httpError
    • state.graphQLErrors
  • client ➡️ const client = useContext(ClientContext) see ClientContext

Not yet supported

  • called

Other

Request interceptors

It is possible to provide a custom library to handle network requests. Having that there is more control on how to handle the requests. The following example shows how to supply axios HTTP client with interceptors. It can be handy in the situations where JWT token has expired, needs to be refreshed and request retried.

import axios from 'axios'
import { buildAxiosFetch } from '@lifeomic/axios-fetch'
import { GraphQLClient } from 'graphql-hooks'

const gqlAxios = axios.create()
gqlAxios.interceptors.response.use(
  function (response) {
    return response
  },
  function (error) {
    // Handle expired JWT and refresh token
  }
)

const client = new GraphQLClient({
  url: '/graphql',
  fetch: buildAxiosFetch(gqlAxios)
})

Contributors

Thanks goes to these wonderful people (emoji key):


Brian Mullan

�� �� �� �� �� �� �� �� �� ⚠️

Jack Clark

�� �� �� �� �� �� �� �� �� ⚠️

Joe Warren

�� �� �� �� �� �� �� �� �� ⚠️

Simone Busoli

�� �� ��

jhey tompkins

⚠️ �� �� �� �� ��

Haroen Viaene

��

Ari Bouius

�� �� �� ⚠️

Klemen Kogovšek

�� �� �� ⚠️

Wésley Queiroz

�� ��

Joseph Thomas

�� �� ⚠️

Edvinas Bartkus

�� �� �� �� �� �� �� �� ⚠️

Matías Olivera

�� �� ⚠️ ��

tcudok-jg

��

Martin Adams

��

Gal Dubitski

�� �� �� ⚠️

Abhishek Shende

�� ��

fabienheureux

��

Hugh Boylan

��

Baqer Mamouri

��

Guillermo Gonzalez

��

Johan Brook

�� �� ��

Peter Balazs

�� �� �� ⚠️

Mattia Panzeri

�� ⚠️

Alex Kondratyuk

�� ⚠️ �� ��

Matias Cepeda

��

Jack Huey

�� �� �� ⚠️

This project follows the all-contributors specification. Contributions of any kind welcome!

  • 前言 公司项目需要使用react,而函数式组件也是官方比较推荐的!!!所以学习hooks是很重要的。 一、什么是函数式组件? 纯函数组件有以下特点: 没有状态 没有生命周期 没有 this 因存在如上特点,使得纯函数组件只能做UI展示的功能, 涉及到状态的管理与切换就不得不用到类组件或者redux。 但因为简单的页面也是用类组件,同时要继承一个React实例,使得代码会显得很重。 以前我们可以使用

  • 前言 刚刚写完后端的代码刷了下B站,发现有很多小伙伴还是觉得国外火的技术传到国内不一定回火,甚至可能会非常沉寂,我个人感觉,学习新的技术不一定目的性那么强,既然它能在国外大火至少说明它的内核很可能值得我们很多人研究好几个月甚至好几年的,抱着更高一层的思想去学习,很多东西我觉得自然而然会得到的. 你们应该知道是什么东西,手动狗头. 数据检查 React和apollo client,甚至graphql

 相关资料
  • High performance Next + React + GraphQL starter kit The purpose of this starter kit is not to be complete solution, but introduction for creating high performance websites with Next.js, React and Grap

  • Angular 2应用程序遍历整个流程集,或者从应用程序的启动到结束都有一个生命周期。 下图显示了Angular 2应用程序生命周期中的整个过程。 以下是每个生命周期钩子的描述。 ngOnChanges - 当数据绑定属性的值更改时,将调用此方法。 ngOnInit - 只要在Angular首次显示数据绑定属性后初始化指令/组件,就会调用此方法。 ngDoCheck - 用于检测并对Angular

  • 常用 Hooks Hooks 是 Rax 1.0 新增的特性,它可以让函数组件(Function Component)使用状态和生命周期。Rax 在实现上遵循了 React Hooks 的标准。 Rax 转小程序链路目前支持的 Hooks 有以下几种: useState useEffect useReducer useContext useRef 关于以上 Hooks 的具体使用方式,可以参考这里

  • Cordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system to customize cordova commands. Hook scripts could be defined by addin

  • 具备基本工程素养的同学都会注重编码规范,而代码风格检查(Code Linting,简称 Lint)是保障代码规范一致性的重要手段。 使用 Lint 会有什么好处呢?在我看来至少具有如下 3 点: 更少的 Bug 更高的开发效率,Lint 很容易发现低级的、显而易见的错误 更高的可读性 很多时候我们lint的校验是放在持续集成阶段,大概流程如下: 代码提交 --> 跑 CI 发现问题(远程) -->

  • 介绍 Cordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system to customize cordova commands. Cordova hooks allow you to perform