{"id":16528479,"url":"https://github.com/rel1cx/react-jsbox","last_synced_at":"2025-08-17T16:36:59.934Z","repository":{"id":34981405,"uuid":"180234202","full_name":"Rel1cx/react-jsbox","owner":"Rel1cx","description":"Custom React renderer for JSBox.","archived":false,"fork":false,"pushed_at":"2025-03-05T03:35:15.000Z","size":602,"stargazers_count":82,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-09T19:19:37.619Z","etag":null,"topics":["jsbox","react","react-reconciler"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Rel1cx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-08T21:16:09.000Z","updated_at":"2025-03-05T03:35:19.000Z","dependencies_parsed_at":"2025-03-16T23:30:29.031Z","dependency_job_id":null,"html_url":"https://github.com/Rel1cx/react-jsbox","commit_stats":null,"previous_names":["nicify/react-jsbox"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rel1cx%2Freact-jsbox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rel1cx%2Freact-jsbox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rel1cx%2Freact-jsbox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rel1cx%2Freact-jsbox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Rel1cx","download_url":"https://codeload.github.com/Rel1cx/react-jsbox/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248094991,"owners_count":21046770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jsbox","react","react-reconciler"],"created_at":"2024-10-11T17:40:32.168Z","updated_at":"2025-04-09T19:19:45.880Z","avatar_url":"https://github.com/Rel1cx.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e A custom React renderer for writing JSBox apps in React.\r\n\r\nnpm: \u003chttps://www.npmjs.com/package/react-jsbox\u003e\r\n\r\nReact doc: \u003chttps://reactjs.org/docs/getting-started.html\u003e\r\n\r\nJSBox doc: \u003chttps://docs.xteko.com/#/en/\u003e\r\n\r\nExample App: \u003chttps://github.com/Nicify/react-jsbox-example\u003e\r\n\r\n## Examples\r\n\r\n### Class\r\n\r\n```jsx\r\nimport React from 'react'\r\nimport { render } from 'react-jsbox'\r\n\r\n// Create React Components:\r\nclass App extends React.PureComponent {\r\n    constructor(props) {\r\n        super(props)\r\n        console.log(props)\r\n        this.state = {\r\n            count: 0\r\n        }\r\n    }\r\n\r\n    render() {\r\n        const { width, height } = this.props\r\n        const styles = {\r\n            container: $rect(0, 0, width, height),\r\n            text: $rect(0, height * 0.25 - 15, width, 30),\r\n            list: $rect(0, height * 0.5, width, 140)\r\n        }\r\n        return (\r\n            \u003cview frame={styles.container}\u003e\r\n                \u003clabel\r\n                    frame={styles.text}\r\n                    align={$align.center}\r\n                    font={$font('ArialRoundedMTBold', 26)}\r\n                    text={String(this.state.count)}\r\n                    autoFontSize={true}\r\n                /\u003e\r\n                \u003clist\r\n                    frame={styles.list}\r\n                    scrollEnabled={false}\r\n                    data={['INCREASE', 'DECREASE', 'RESET']}\r\n                    events={{\r\n                        didSelect: (sender, { row }, data) =\u003e\r\n                            this.setState({\r\n                                count: this.state.count + [1, -1, -this.state.count][row]\r\n                            })\r\n                    }}\r\n                /\u003e\r\n            \u003c/view\u003e\r\n        )\r\n    }\r\n}\r\n\r\n// Create JSBox root container and render the React component into it\r\n$ui.render({\r\n    props: {\r\n        title: '',\r\n        debugging: true\r\n    },\r\n    views: [\r\n        {\r\n            type: 'view',\r\n            props: {\r\n                id: 'root'\r\n            },\r\n            layout(make, view) {\r\n                make.edges.equalTo(view.super.safeArea)\r\n            },\r\n            events: {\r\n                layoutSubviews(view) {\r\n                    const { width, height } = view.frame\r\n                    render(\u003cApp width={width} height={height} /\u003e, view)\r\n                }\r\n            }\r\n        }\r\n    ]\r\n})\r\n```\r\n\r\n#### Use ref to access JSBox view instance\r\n\r\n```jsx\r\nimport React from 'react'\r\nimport { render } from 'react-jsbox'\r\n\r\nclass App extends React.PureComponent {\r\n    constructor(props) {\r\n        super(props)\r\n        this.state = {\r\n            text: ''\r\n        }\r\n        this._input = React.createRef()\r\n        this._handleTextChange = this.handleTextChange.bind(this)\r\n    }\r\n\r\n    handleTextChange(sender) {\r\n        this.setState({ text: sender.text })\r\n    }\r\n\r\n    componentDidMount() {\r\n        this._input.current.focus()\r\n    }\r\n\r\n    render() {\r\n        const { width, height } = this.props\r\n        const styles = {\r\n            container: $rect(0, 0, width, height - 40),\r\n            text: $rect(0, 64, width, 30),\r\n            textInput: $rect(10, 160, width - 20, 48)\r\n        }\r\n        return (\r\n            \u003cview\r\n                id=\"container\"\r\n                frame={styles.container}\r\n                bgcolor={$color('#2ac')}\r\n                events={{\r\n                    tapped: () =\u003e this._input.current.blur()\r\n                }}\r\n            \u003e\r\n                \u003clabel\r\n                    id={'text'}\r\n                    frame={styles.text}\r\n                    align={$align.center}\r\n                    font={$font('ArialRoundedMTBold', 26)}\r\n                    text={this.state.text || 'Hello World!'}\r\n                    textColor={$color('#fff')}\r\n                    autoFontSize={true}\r\n                /\u003e\r\n                \u003cinput\r\n                    id={'textInput'}\r\n                    // use ref to access JSBox view instance\r\n                    ref={this._input}\r\n                    frame={styles.textInput}\r\n                    font={$font('ArialRoundedMTBold', 24)}\r\n                    tintColor={$color('orange')}\r\n                    placeholder={'Type here...'}\r\n                    events={{\r\n                        changed: this._handleTextChange\r\n                    }}\r\n                /\u003e\r\n            \u003c/view\u003e\r\n        )\r\n    }\r\n}\r\n\r\n$ui.render({\r\n    props: {\r\n        title: '',\r\n        debugging: true\r\n    },\r\n    views: [\r\n        {\r\n            type: 'view',\r\n            props: {\r\n                id: 'root'\r\n            },\r\n            layout(make, view) {\r\n                make.edges.equalTo(view.super.safeArea)\r\n            },\r\n            events: {\r\n                layoutSubviews(view) {\r\n                    const { width, height } = view.frame\r\n                    render(\u003cApp width={width} height={height} /\u003e, view)\r\n                }\r\n            }\r\n        }\r\n    ]\r\n})\r\n```\r\n\r\n### Hooks\r\n\r\n#### useReducer\r\n\r\n```jsx\r\nimport React, { useMemo } from 'react'\r\nimport { render } from 'react-jsbox'\r\n\r\nfunction App({ width, height }) {\r\n    const [state, dispatch] = React.useReducer(counterReducer, { count: 0 })\r\n\r\n    const styles = useMemo(\r\n        () =\u003e ({\r\n            container: $rect(0, 0, width, height),\r\n            text: $rect(0, height * 0.25 - 15, width, 30),\r\n            list: $rect(0, height * 0.5, width, 140)\r\n        }),\r\n        [width, height]\r\n    )\r\n\r\n    return (\r\n        \u003cview frame={styles.container}\u003e\r\n            \u003clabel\r\n                frame={styles.text}\r\n                align={$align.center}\r\n                font={$font('ArialRoundedMTBold', 26)}\r\n                text={String(state.count)}\r\n                autoFontSize={true}\r\n            /\u003e\r\n            \u003clist\r\n                frame={styles.list}\r\n                scrollEnabled={false}\r\n                data={['INCREASE', 'DECREASE', 'RESET']}\r\n                events={{\r\n                    didSelect: (sender, indexPath, data) =\u003e dispatch({ type: data })\r\n                }}\r\n            /\u003e\r\n        \u003c/view\u003e\r\n    )\r\n}\r\n\r\nconst counterReducer = (state, action) =\u003e {\r\n    switch (action.type) {\r\n        case 'INCREASE':\r\n            return { ...state, count: state.count + 1 }\r\n        case 'DECREASE':\r\n            return { ...state, count: state.count - 1 }\r\n        case 'RESET':\r\n            return { ...state, count: 0 }\r\n        default:\r\n            throw new Error()\r\n    }\r\n}\r\n\r\n$ui.render({\r\n    props: {\r\n        title: '',\r\n        debugging: true\r\n    },\r\n    views: [\r\n        {\r\n            type: 'view',\r\n            props: {\r\n                id: 'root'\r\n            },\r\n            layout(make, view) {\r\n                make.edges.equalTo(view.super.safeArea)\r\n            },\r\n            events: {\r\n                layoutSubviews(view) {\r\n                    const { width, height } = view.frame\r\n                    render(\u003cApp width={width} height={height} /\u003e, view)\r\n                }\r\n            }\r\n        }\r\n    ]\r\n})\r\n```\r\n\r\n#### useEffect\r\n\r\nIn **useCache.js**\r\n\r\n```jsx\r\nimport { useEffect, useState } from 'react'\r\n\r\nconst useCache = (key, initialValue) =\u003e {\r\n    const [state, setState] = useState(() =\u003e {\r\n        const cacheValue = $cache.get(key)\r\n        if (cacheValue === undefined) {\r\n            $cache.set(key, initialValue)\r\n            return initialValue\r\n        }\r\n        return cacheValue\r\n    })\r\n    useEffect(() =\u003e $cache.set(key, state))\r\n\r\n    return [state, setState]\r\n}\r\n\r\nexport default useCache\r\n```\r\n\r\nIn **app.js**\r\n\r\n```jsx\r\nimport React, { useMemo } from 'react'\r\nimport { render, useCache } from 'react-jsbox'\r\nimport useCache from './useCache'\r\n\r\nfunction App({ width, height }) {\r\n    const [count, setCount] = useCache('count', 0)\r\n\r\n    const styles = useMemo(\r\n        () =\u003e ({\r\n            container: $rect(0, 0, width, height),\r\n            text: $rect(0, height * 0.25 - 15, width, 30),\r\n            list: $rect(0, height * 0.5, width, 140)\r\n        }),\r\n        [width, height]\r\n    )\r\n\r\n    return (\r\n        \u003cview frame={styles.container}\u003e\r\n            \u003clabel\r\n                frame={styles.text}\r\n                align={$align.center}\r\n                font={$font('ArialRoundedMTBold', 26)}\r\n                text={String(count)}\r\n                autoFontSize={true}\r\n            /\u003e\r\n            \u003clist\r\n                frame={styles.list}\r\n                scrollEnabled={false}\r\n                data={['INCREASE', 'DECREASE', 'RESET']}\r\n                events={{\r\n                    didSelect(sender, { row }) {\r\n                        setCount(x =\u003e x + [1, -1, -x][row])\r\n                    }\r\n                }}\r\n            /\u003e\r\n        \u003c/view\u003e\r\n    )\r\n}\r\n\r\n$ui.render({\r\n    props: {\r\n        title: '',\r\n        debugging: true\r\n    },\r\n    views: [\r\n        {\r\n            type: 'view',\r\n            props: {\r\n                id: 'root'\r\n            },\r\n            layout(make, view) {\r\n                make.edges.equalTo(view.super.safeArea)\r\n            },\r\n            events: {\r\n                layoutSubviews(view) {\r\n                    const { width, height } = view.frame\r\n                    render(\u003cApp width={width} height={height} /\u003e, view)\r\n                }\r\n            }\r\n        }\r\n    ]\r\n})\r\n```\r\n\r\n#### JsxLiteral\r\n\r\n```javascript\r\nconst htm = require('htm')\r\nconst { createElement, useState, useMemo } = require('react')\r\nconst ReactJSBox = require('react-jsbox')\r\nconst jsx = htm.bind(createElement)\r\n\r\nfunction JsxLiteralExample({ width, height }) {\r\n    const [count, setCount] = useState(0)\r\n\r\n    const styles = useMemo(\r\n        () =\u003e ({\r\n            container: $rect(0, 0, width, height),\r\n            text: $rect(0, height * 0.25 - 15, width, 30),\r\n            list: $rect(0, height * 0.5, width, 140)\r\n        }),\r\n        [width, height]\r\n    )\r\n\r\n    return jsx`\u003cview frame=${styles.container}\u003e\r\n      \u003clabel\r\n        frame=${styles.text}\r\n        align=${$align.center}\r\n        font=${$font(26)}\r\n        text=${String(count)}\r\n        autoFontSize=${true}\r\n      /\u003e\r\n      \u003clist\r\n        frame=${styles.list}\r\n        scrollEnabled=${false}\r\n        data=${['INCREASE', 'DECREASE', 'RESET']}\r\n        events=${{\r\n            didSelect: (sender, { row }) =\u003e {\r\n                setCount(x =\u003e x + [1, -1, -x][row])\r\n            }\r\n        }}\r\n      /\u003e\r\n    \u003c/view\u003e`\r\n}\r\n\r\n$ui.render({\r\n    props: {\r\n        title: 'JsxLiteralExample'\r\n    },\r\n    views: [\r\n        {\r\n            type: 'view',\r\n            props: {\r\n                id: 'root'\r\n            },\r\n            layout(make, view) {\r\n                make.edges.equalTo(view.super.safeArea)\r\n            },\r\n            events: {\r\n                layoutSubviews(view) {\r\n                    const { width, height } = view.frame\r\n                    ReactJSBox.render(jsx`\u003c${JsxLiteralExample} width=${width} height=${height} /\u003e`, view)\r\n                }\r\n            }\r\n        }\r\n    ]\r\n})\r\n```\n\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frel1cx%2Freact-jsbox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frel1cx%2Freact-jsbox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frel1cx%2Freact-jsbox/lists"}