如何在完成发布后获得通知 [英] How to get notified when a Post is done with ketting

查看:63
本文介绍了如何在完成发布后获得通知的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我实际上正在尝试使用fastify的API进行反应保持功能.

I'm actually trying the power of react-ketting with a fastify's API.

从钩子useResource上,我可以获取一个将进行POST的Submit函数,但是我不知道该提交何时完成,并且无法获得该POST的API答复.

From the hook useResource, I can get a submit function that will make a POST but I can't know when that submit is done and I can't get the API reply of this POST.

反应不允许这样做吗?我是否错过了参数,还是必须使用其他类似Axios的PUT/POST才能拥有全部控制权?

React doesn't allow this? Did I miss a param or must I use something else for PUT/POST like axios in order to have all control ?

// API - with Fastify

// server.js
// Require the framework and instantiate it
const fastify = require('fastify')({
  logger: true
})

fastify.register(require('fastify-cors'), {
  origin: true,
  allowedHeaders: [
    'Content-Type',
    'User-Agent',
    'Authorization',
    'Accept',
    'Prefer',
    'Link'
  ],
  methods: [
    'DELETE',
    'GET',
    'PATCH',
    'POST',
    'PUT',
    'HEAD'
  ],
  exposedHeaders: [
    'Location',
    'Link'
  ]
});

// Loading routes
fastify.register(require('./routes/Products'));

// Run the server!
fastify.listen(5000, '0.0.0.0', function (err, address) {
  if (err)
  {
    fastify.log.error(err)
    process.exit(1)
  }
  fastify.log.info(`server listening on ${address}`)
});

// ./routes/Products.js
async function routes(fastify, options) {

  // Root
  fastify.get('/', async (request, reply) => {
    // Relations
    resource
      .addLink('collection', { href: `/products`, title: 'Products' });

    reply.send(resource);
  });

  // List all products
  fastify.get('/products', async (request, reply) => {
    const resource = halson();

    // Relations
    resource
      .addLink('self', { href: '/products', title: 'Products' })

    // Collection items
    const products = DB.getProducts(); // [{id: 'xxx', name: 'yyy'}, ...]

    if (products && products.length) {
      products.forEach(product => {
        resource.addLink('item', { href: `/products/${product.id}`, title: product.name });
      });
    }

    // Actions like
    resource.addLink('create', { href: '/products/add', title: 'Add product' })

    reply.send(resource);
  })

  // Get product
  fastify.get('/products/:productId', async (request, reply) => {
    const productId = request.params.productId;
    const product = DB.getProductById(productId); // { id: 'xxx', name: 'yyy', ... }
    const resource = halson(product);

    // Relations
    resource
      .addLink('self', { href: `/products/${productId}`, title: `${product.name}` })

    reply.send(resource);
  });

  // Get add product form
  fastify.get('/products/add', async (request, reply) => {
    const resource = halson();

    // Relations
    resource
      .addLink('create-form', { href: 'addProductForm' })

    // Embeded resource
    const initialData = {
      productId: uuidv4(),
      name: ''
    };

    const embed = halson({
      submitData: {
        resource: '/products/add',
        mode: 'POST',
        initialData: initialData,
      },
      jsonSchema: {
        type: 'object',
        title: 'Add a product',
        required: ['name'],
        properties: {
          productId: {
            type: 'string',
            title: 'Product ID'
          },
          name: {
            type: 'string',
            title: 'Product name'
          }
        }
      },
      uiSchema: {
        productId: {
          "ui:readonly": true
        },
        name: {
          "ui:autofocus": true
        }
      },
      formData: initialData
    });

    embed.addLink('self', { href: 'addProductForm' })

    resource.addEmbed('create-form', embed);

    reply.send(resource);
  });

  // Post new product
  fastify.post('/products/add', async (request, reply) => {
    const product = DB.addProduct(request.body); // { id: 'xxx', name: 'yyy', ... }

    reply
      .code(201)
      .header('Location', `/products/${product.id}`)
      .send(halson());
  });

}



// Front

// index.jsx
import { Client } from 'ketting';
import { KettingProvider } from 'react-ketting';

const BASE_URL = 'http://localhost:5000';
const KettingClient = new Client(BASE_URL);

// HOC useResource
const withUseResource = WrappedComponent => ({ resource, ...props }) => {
  const {
    loading,
    error,
    data,
    setData,
    submit,
    resourceState,
    setResourceState,
  } = useResource(resource);

  if (loading) return 'Loading ...';
  if (error) return `Error : ${error.message}`;

  const { children } = props;

  const newProps = {
    resource,
    resourceData: data,
    setResourceData: setData,
    submitResourceData: submit,
    resourceState,
    setResourceState,
  };

  return (
    <WrappedComponent {...newProps} {...props}>
      {children}
    </WrappedComponent>
  );
};

// HOC useCollection
const withUseCollection = WrappedComponent => ({ resource, ...props }) => {
  const [collection, setCollection] = useState([]);
  const { loading, error, items } = useCollection(resource);

  useEffect(() => {
    setCollection(items);

    return () => {
      setCollection([]);
    };
  }, [items]);

  if (loading) return 'Loading ...';
  if (error) return `Error : ${error.message}`;

  const { children } = props;

  const newProps = {
    resource,
    collection,
  };

  return (
    <WrappedComponent {...newProps} {...props}>
      {children}
    </WrappedComponent>
  );
};

// Main Component
function Root() {
  return (
    <KettingProvider client={KettingClient}>
      <Container />
    </KettingProvider>
  );
}


function Container() {
  const [url, setUrl] = useState(`${BASE_URL}/`);
  const [resource, setResource] = useState(null);

  useEffect(() => {
    setResource(KettingClient.go(url));
  }, [url]);

  if (!resource) {
    return null;
  }

  return <Content resource={resource} />;
}

const SimpleContent = ({ resource, resourceState }) => {
  let content = null;

  if (resourceState.links.has('collection')) {
    content = <Catalog resource={resource.follow('collection')} />;
  }

  return content;
};

const Content = withUseResource(SimpleContent);

const SimpleCatalog = ({ resource, resourceState, collection }) => {
  const [addProductBtn, setAddProductBtn] = useState(null);

  useEffect(() => {
    if (resourceState?.links.has('create')) {
      setAddProductBtn(
        <AddNewProduct resource={resource.follow('create')} />
      );
    }
  }, []);

  return (
    <>
      {collection.map((item, index) => (
        <Product key={index} resource={item} />
      ))}
      {addProductBtn}
    </>
  );
};

const Catalog = withUseResource(withUseCollection(SimpleCatalog));

const SimpleProduct = ({ resourceData }) => {
  return <p>{resourceData.name}</p>;
};

const Product = withUseResource(SimpleProduct);

const SimpleNewProduct = ({ resource, resourceState }) => {
  const [openDialog, setOpenDialog] = useState(false);
  const [dialogConfig, setDialogConfig] = useState({});

  useEffect(() => {
    if (resourceState.links.has('create-form')) {
      setDialogConfig({
        title: '',
        content: (
          <div>
            <FormDialog
              resource={resource.follow('create-form')}
              setOpenDialog={setOpenDialog}
            />
          </div>
        ),
        actions: '',
      });
    }

    return () => {
      setDialogConfig({});
    };
  }, []);

  return (
    <>
      <Button onClick={() => setOpenDialog(true)}>+</Button>
      <PopupDialog
        config={dialogConfig}
        open={openDialog}
        onClose={() => setOpenDialog(false)}
      />
    </>
  );
};

const AddNewProduct = withUseResource(SimpleNewProduct);

const SimpleFormDialog = ({ resourceData, setOpenDialog }) => {
  const { jsonSchema, uiSchema, formData, submitData } = resourceData;
  const { loading, error, setData, submit } = useResource(
    submitData
  );

  if (loading) return 'Loading ...';
  if (error) return `Error : ${error.message}`;

  const handleChange = fd => {
    setData(fd);
  };

  const handleSubmit = () => {
    // How to get notified that the post has been processed,
    // so that I can go to the new product page,
    // or have the catalog to be refreshed ?
    submit();
    setOpenDialog(false);
  };

  return (
    <div>
      { /* React JsonSchema Form */ }
      <RjsfForm
        JsonSchema={jsonSchema}
        UiSchema={uiSchema}
        formDataReceived={formData}
        handleChange={handleChange}
        handleSubmit={handleSubmit}
      />
    </div>
  );
};

const FormDialog = withUseResource(SimpleFormDialog);

const PopupDialog = ({ open, onClose, config }) => {
  if (!config) return null;

  const { title, content, actions, props } = config;

  return (
    <Dialog fullWidth maxWidth='md' open={open} onClose={onClose} {...props}>
      <DialogTitle>{title}</DialogTitle>
      <DialogContent>{content}</DialogContent>
      <DialogActions>{actions}</DialogActions>
    </Dialog>
  );
};

推荐答案

使用Ketting进行POST请求的主要原因/过程有两个.

There's two main reasons/processes to do POST requests with Ketting.

  1. 要创建新资源/将新资源添加到集合中.
  2. 进行任意的RPC调用/提交表单.

创建新资源

使用 useResource()钩子并使用 submit()函数时,此方法的主要目的是处理第一种情况.

Creating a new resource

When you use the useResource() hook, and use the submit() function, the primary purpose of this is to deal with the first case.

因为您严格地在制作新资源,所以期望该响应包含:

Because you are strictly making a new resource, the expectation is that the response contains:

  1. 201已创建状态
  2. 指向新创建的资源的 Location 标头.

(可选)服务器可以返回新创建的资源的主体,但为此,服务器还必须包含 Content-Location 标头.

Optionally, you the server can return the body of the newly created resource, but to do that, the server must also include a Content-Location header.

如果这是您的目的,则只需侦听您已经拥有的组件中的状态变化,就可以得到 POST 请求的结果.

If that was your purpose, you can get the result of the POST request simply by listening to state changes in the component you already have.

如果您属于第二类,并且只想执行任意POST请求并读取响应,则应该使用从代码返回的 submit()函数钩子.

If you are in the second category and just want to do an arbitrary POST request and read the response, you should not use the submit() function returned from the hook.

useResource 钩子上的 submit()函数实际上用作资源状态提交"机制,对于其他任意 POST 请求.

The submit() function on the useResource hook is really used as a 'resource state submissions' mechanism, and it's not good to overload this for other arbitrary POST requests.

相反,只需在 Resource 本身上使用 .post()函数.

Instead, just use the .post() function on the Resource itself.

const response = await props.resource.post({
  data: body
});

这篇关于如何在完成发布后获得通知的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆