diff --git a/doc/InteropWithReact.md b/doc/InteropWithReact.md index 4739d4e..99eacf9 100644 --- a/doc/InteropWithReact.md +++ b/doc/InteropWithReact.md @@ -183,6 +183,10 @@ to always put the call at the top, as in `my-div` here: [:p "Some other text in bold."]]]) ``` +## React Features + +- [React Features and how to use them in Reagent](./ReactFeatures.md) + ## Examples - [Material-UI](../examples/material-ui/src/example/core.cljs) diff --git a/doc/ReactFeatures.md b/doc/ReactFeatures.md new file mode 100644 index 0000000..b36827a --- /dev/null +++ b/doc/ReactFeatures.md @@ -0,0 +1,92 @@ +# React Features + +Most React features should be usable from Reagent, even if Reagent doesn't +provide functions to use them directly. + +## Context + +```cljs +(defonce my-context (react/createContext "default")) + +(def Provider (.-Provider my-context)) +(def Consumer (.-Consumer my-context)) + +(r/render [:> Provider {:value "bar"} + [:> Consumer {} + (fn [v] + (r/as-element [:div "Context: " v]))]] + container) +``` + +Tests contain example of using old React lifecycle Context API (`context-wrapper` function): +[tests](https://github.com/reagent-project/reagent/blob/master/test/reagenttest/testreagent.cljs#L1141-L1165) + +## Error boundaries + +You can use `ComponentDidCatch` lifecycle method with `create-class`: + +```cljs +(defn error-boundary [comp] + (r/create-class + {:component-did-catch (fn [this e info] + (reset! error e)) + :reagent-render (fn [comp] + (if @error + [:div + "Something went wrong." + [:button {:on-click #(reset! error nil)} "Try again"]] + comp))})) +``` + +## Hooks + +Hooks can't be used inside class components, and Reagent implementation creates +a class component from every function (i.e. Reagent component). + +However, you can use React components using Hooks inside Reagent, or use +[hx](https://github.com/Lokeh/hx) components inside Reagent. Also, it is +possible to create React components from Reagent quite easily, because React +function component is just a function that happens to return React elements, +and `r/as-element` does just that: + +```cljs +;; This is React function component. Can't use Ratoms here! +(defn example [] + (let [[count set-count] (react/useState 0)] + (r/as-element + [:div + [:p "You clicked " count " times"] + [:button + {:on-click #(set-count inc)} + "Click"]]))) + +;; Reagent component +(defn reagent-component [] + [:div + ;; Note :> to use a function as React component + [:> example]]) +``` + +If you need to pass RAtom state into these components, dereference them in +the Reagent components and pass the value (and if needed, function to update them) +as properties into the React function component. + +## Portals + +```cljs +(defn reagent-component [] + (r/create-class + {:render (fn [this] + (let [el (.. js/document (getElementById "portal-el"))] + (react-dom/createPortal (r/as-element [:div "foo"]) el)))})) + +``` + +TODO: Can this be done without create-class and `:render`. +TODO: This might have problems handling Ratoms, test. + +## Hydrate + +```cljs +(react-dom/hydrate (r/as-element [main-component]) container) +``` diff --git a/doc/cljdoc.edn b/doc/cljdoc.edn index 97581a2..03fece6 100644 --- a/doc/cljdoc.edn +++ b/doc/cljdoc.edn @@ -7,7 +7,8 @@ ["When do components update?" {:file "doc/WhenDoComponentsUpdate.md"}] ["[WIP] Managing State: atoms, cursors, Reactions, and tracking" {:file "doc/ManagingState.md"}] ["Batching and Timing: How Reagent Renders Changes to Application State" {:file "doc/BatchingAndTiming.md"}] - ["Interop with React" {:file "doc/InteropWithReact.md"}]] + ["Interop with React" {:file "doc/InteropWithReact.md"}] + ["React Features" {:file "doc/ReactFeatures.md"}]] ["Frequently Asked Questions" {} ["Why isn't my Component re-rendering?" {:file "doc/FAQ/ComponentNotRerendering.md"}] ["How do I use React's \"refs\"" {:file "doc/FAQ/UsingRefs.md"}]