{"id":"firere/roact-hooks","name":"roact-hooks","scope":"firere","platform":"roblox","description":"An implementation of hooks in Roact, with some extra features","version":"0.5.2","latest":"0.5.2","versions":["0.5.2"],"license":"MPL-2.0","licenseRating":"caution","licenseCaveats":["File-level copyleft: if you modify this package's own source files, those modified files must be made available under MPL-2.0. Using it unmodified in a closed-source game is fine."],"licenseVerified":false,"dependencies":{},"integrity":"a40f292acc1ba27ca32b3c892c4aeb18853ca0f564daecbf2df9dea73404ae48","likes":0,"downloads":0,"install":"forest install firere/roact-hooks","url":"https://forest.dev/p/roblox/firere/roact-hooks","files":"https://api.forest.dev/ai/package/roblox/firere/roact-hooks/files","readme":"# roact-hooks\r\nAn implementation of [React hooks](https://reactjs.org/docs/hooks-intro.html) for [Roact](https://github.com/Roblox/roact). Does not make any modifications to Roact itself.\r\n\r\n## Example\r\n```lua\r\nlocal Hooks = require(ReplicatedStorage.Hooks)\r\nlocal Roact = require(ReplicatedStorage.Roact)\r\n\r\n-- `props` are our normal passed in properties.\r\n-- `hooks` is passed in by roact-hooks itself.\r\nlocal function Example(props, hooks)\r\n\tlocal count, setCount = hooks.useState(0)\r\n\r\n\thooks.useEffect(function()\r\n\t\tprint(\"the count is\", count)\r\n\tend)\r\n\r\n\treturn Roact.createElement(Button, {\r\n\t\tonClick = function()\r\n\t\t\tsetCount(count + 1)\r\n\t\tend,\r\n\r\n\t\ttext = count,\r\n\t})\r\nend\r\n\r\n-- This returns a component that you can call `Roact.createElement` with\r\nExample = Hooks.new(Roact)(Example)\r\n```\r\n\r\n## API\r\n### Hooks.new\r\n```\r\nHooks.new(Roact: Roact) -> (render: (props, hooks) -> RoactComponent | nil, options?: {\r\n\tname?: string,\r\n\tdefaultProps?: Map<any, any>,\r\n\tcomponentType?: string,\r\n\tvalidateProps?: (props) -> (false, message: string) | true,\r\n}) -> RoactComponent)\r\n```\r\n\r\nIt is required you pass in the Roact you are using, since you can't combine multiple versions of Roact together.\r\n\r\nReturns a function that can be used to create a new Roact component with hooks. An optional dictionary can be passed in. The following are the valid keys that can be used, and what they do.\r\n\r\n#### name\r\nRefers to the name used in debugging. If it is not passed, it'll use the function name of what was passed in. For instance, `Hooks.new(Roact)(Component)` will have the component name `\"Component\"`.\r\n\r\n#### defaultProps\r\nDefines default values for props to ensure props will have values even if they were not specified by the parent component.\r\n\r\n## Implemented Hooks\r\n\r\n### useState\r\n`useState<T>(defaultValue: T | (() -> T)) -> (T, update: (value: T | ((prevState: T) -> T)) -> ())`\r\n\r\nUsed to store a stateful value. Returns the current value, and a function that can be used to set the value.\r\n\r\n### useEffect\r\n`useEffect(callback: () -> (() -> void)?, dependencies?: any[])`\r\n\r\nUsed to perform a side-effect with a callback function.\r\n\r\nThis callback function can return a destructor. When the component unmounts or the dependencies change, this function will be called.\r\n\r\nYou can also pass in a list of dependencies to `useEffect`. If passed, then only when those dependencies change will the callback function be re-ran. Additionally, if you pass `\"didMount\"` as a dependency, then the effect will not run when the component mounts; it will truly only run the callback whenever one of the other dependencies changes.\r\n\r\n### useContext\r\n`useContext(context: RoactContext<T>) -> T`\r\n\r\nReturns the value of the [context](https://roblox.github.io/roact/advanced/context/).\r\n\r\n### useValue\r\n`useValue(value: T) -> { value: T }`\r\n\r\nSimilar to [useRef in React](https://reactjs.org/docs/hooks-reference.html#useref). Creates a table that you can mutate without re-rendering the component every time. Think of it like a class variable (`self.something = 1` vs. `self:setState({ something = 1 })`).\r\n\r\n### useCallback\r\n`useCallback<F: (...args: any[]) -> any>(callback: F, dependencies: any[]): F`\r\n\r\nReturns a [memoized](https://en.wikipedia.org/wiki/Memoization) callback.\r\n\r\n`useCallback(callback, dependencies)` is equivalent to `useMemo(function() return callback end, dependencies)`.\r\n\r\n### useMemo\r\n`useMemo(createValue: () -> T, dependencies: any[]): T`\r\n\r\nReturns a [memoized](https://en.wikipedia.org/wiki/Memoization) value.\r\n\r\n`useMemo` will only recalculate the inner value when the dependencies have changed.\r\n\r\nThe function passed to `useMemo` runs during rendering, so don't perform any side effects.\r\n\r\nIf no array is provided, a new value will be computed on every render.\r\n\r\n### useBinding\r\n`useBinding(defaultValue: T) -> RoactBinding<T>, (newValue: T) -> void`\r\n\r\nReturns a [memoized](https://en.wikipedia.org/wiki/Memoization) [binding](https://roblox.github.io/roact/advanced/bindings-and-refs/#bindings).\r\n\r\nThese can then be used just like normal bindings in Roact.\r\n\r\n### useReducer\r\n`useReducer(reducer: (state: T, action: A), initialState: T) -> (T, (action: A) -> void)`\r\n\r\nAn alternative to `useState` that uses a reducer rather than state directly. If you’re familiar with Rodux, you already know how this works.\r\n\r\n```lua\r\nlocal initialState = { count = 0 }\r\n\r\nlocal function reducer(state, action)\r\n\tif action.type == \"increment\" then\r\n\t\treturn {\r\n\t\t\tcount = state.count + 1,\r\n\t\t}\r\n\telseif action.type == \"decrement\" then\r\n\t\treturn {\r\n\t\t\tcount = state.count - 1,\r\n\t\t}\r\n\telse\r\n\t\terror(\"Unknown type: \" .. tostring(action.type))\r\n\tend\r\nend\r\n\r\nlocal function Counter(_props, hooks)\r\n\tlocal state, dispatch = hooks.useReducer(reducer, initialState)\r\n\r\n\treturn e(Frame, {}, {\r\n\t\tCounter = e(Text, {\r\n\t\t\ttext = state.count,\r\n\t\t}),\r\n\r\n\t\tIncrement = e(Button, {\r\n\t\t\tonClick = function()\r\n\t\t\t\tdispatch({\r\n\t\t\t\t\ttype = \"increment\",\r\n\t\t\t\t})\r\n\t\t\tend,\r\n\t\t}),\r\n\r\n\t\tDecrement = e(Button, {\r\n\t\t\tonClick = function()\r\n\t\t\t\tdispatch({\r\n\t\t\t\t\ttype = \"decrement\",\r\n\t\t\t\t})\r\n\t\t\tend,\r\n\t\t}),\r\n\t})\r\nend\r\n```\r\n\r\n### Roact\r\nRoact is also provided in the hooks argument. This is useful if custom hooks need direct access to Roact.\r\n```lua\r\n-- useCustomHook.lua\r\nlocal function useCustomHook(hooks)\r\n\tlocal Roact = hooks.Roact\r\nend\r\n\r\n-- Example.lua\r\nlocal function Example(props, hooks)\r\n\tlocal example = useCustomHook(hooks)\r\n\treturn nil\r\nend\r\n```\r\n\r\n## Rules of Hooks\r\nThe rules of roact-hooks are the same as [those found in React](https://reactjs.org/docs/hooks-rules.html).\r\n\r\n### Don't call hooks conditionally or in loops.\r\nCall all hooks from the top level of your function. Do not use them in loops or conditions.\r\n\r\n### Only call hooks from Roact functions.\r\n\r\nYou can only call hooks from:\r\n- Roact function components\r\n- Custom hooks (a function that begins with the word `use`)\r\n","readmeTruncated":false}