Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/caiyongmin/tiny-react
React personal implementation version with unit test.
https://github.com/caiyongmin/tiny-react
diff hooks react typescript
Last synced: 2 months ago
JSON representation
React personal implementation version with unit test.
- Host: GitHub
- URL: https://github.com/caiyongmin/tiny-react
- Owner: caiyongmin
- License: mit
- Created: 2019-03-31T04:04:00.000Z (almost 6 years ago)
- Default Branch: master
- Last Pushed: 2023-01-07T07:54:07.000Z (about 2 years ago)
- Last Synced: 2024-04-12T16:33:53.186Z (9 months ago)
- Topics: diff, hooks, react, typescript
- Language: TypeScript
- Homepage:
- Size: 1.3 MB
- Stars: 3
- Watchers: 2
- Forks: 1
- Open Issues: 18
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# creact
[![npm version](https://img.shields.io/npm/v/@caiym/react.svg?style=flat)](https://www.npmjs.com/package/@caiym/react) [![codecov](https://codecov.io/gh/caiyongmin/creact/branch/master/graph/badge.svg)](https://codecov.io/gh/caiyongmin/creact) ![](https://badgen.net/bundlephobia/minzip/@caiym/react) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](README.md)
[React](https://reactjs.org/) personal implementation version.
## Run
```bash
yarn# need install parcel:
# - yarn global add parcel-bundler
# - npm install -g parcel-bundler
npm start
```## Features
- Familiar React render component with virtual dom.
- Support `useState` hook.## Todos
- [x] Support render `Class Component`.
- [x] Support Class Component `setState` and `lifecycle api`.
- [ ] Support other hooks api.
- [x] support `useEffect`.
- [x] support `useReducer`.
- [x] support `useCallback` / `useMemo` / `useRef`.
- [x] support `useContext`.
- [x] Publish package.
- [x] Add unit test.
- [ ] Clarify the code design and add necessary comments.## Examples
- [render Function Component and Class Component](#render-function-component-and-class-component).
- [render useState Component](#render-usestate-component).
- [render useEffect Component](#render-useeffect-component).
- [render useReducer Component](#render-usereducer-component).
- [render useCallback Component](#render-usecallback-component).
- [render useMemo Component](#render-usememo-component).
- [render useRef Component](#render-useref-component).
- [render useContext Component](#render-usecontext-component).### render Function Component and Class Component
↥ back to examples[![Edit creact-simple-demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/epic-water-d6t2b?fontsize=14)
#### Function Component
```tsx
import { React, useState } from "@caiym/react";function Event() {
return Event;
}function Log() {
return Log;
}export default function HooksFunction() {
const [toggle, setToggle] = useState(false);return (
{
setToggle(!toggle);
}}
>
useState toggle
{toggle ? : }
);
}
```#### Class Component
```tsx
import { React } from "@caiym/react";type ItemType = {
id: number;
text: string;
};interface TodoAppProps {
title: string;
}interface TodoAppState {
text: string;
items: ItemType[];
}class TodoApp extends React.Component {
constructor(props: TodoAppProps) {
super(props);this.state = {
items: [],
text: "",
};
}handleChange = (e: MouseEvent) => {
const target = e.target as HTMLInputElement;
this.setState({ text: target.value });
}handleSubmit = (e: MouseEvent) => {
const { text } = this.state;e.preventDefault();
if (!text.length) {
return;
}
const newItem = {
text,
id: Date.now(),
};
this.setState((state: TodoAppState) => ({
items: state.items.concat(newItem),
text: '',
}));
}render() {
const { title } = this.props;
const { items, text } = this.state;return (
{title}
What needs to be done?
Add #{items.length + 1}
);
}
}interface TodoListProps {
items: ItemType[];
}class TodoList extends React.Component {
render() {
const { items } = this.props;return (
{items.map((item: ItemType) => (
- {item.text}
))}
);
}
}export default TodoApp;
```#### Render
```tsx
import { React, ReactDOM } from "@caiym/react";
import FunctionComponent from "./FunctionComponent";
import ClassComponent from "./ClassComponent";class App extends React.Component {
render() {
return (
);
}
}ReactDOM.render(, document.getElementById("root"));
```### render useState Component
↥ back to examples[![Edit creact useState demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/creact-usestate-demo-njttc?fontsize=14)
```tsx
import { useState } from "@caiym/react";function UseStateComponent() {
const initialValue = 0;
const [ count, setCount ] = useState(initialValue);return (
useState
setCount(count + 1)}>setCount
setCount(initialValue)}>reset
{count}
);
}
```### render useEffect Component
↥ back to examples[![Edit creact useEffect demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/creact-useeffect-demo-nqt27?fontsize=14)
```tsx
import { useState, useEffect } from "@caiym/react";function UseEffectComponent() {
const [ toggle, setToggle ] = useState(false);
const [ count, setCount ] = useState(0);useEffect(() => {
console.info('===run useEffect function===');
return () => {
console.info('===cleanup before re-run useEffect function===');
};
}, [toggle]);return (
useEffect
需要打开控制台查看运行结果
setToggle(!toggle)}>
setToggle trigger run useEffect function, toggle: {String(toggle)}
setCount(count + 1)}>
setCount don't trigger run useEffect function, count: {count}
);
}
```### render useReducer Component
↥ back to examples[![Edit creact useReducer demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/creact-usereducer-demo-fvz45?fontsize=14)
```tsx
import { useReducer } from "@caiym/react";const sleep = (ms: number = 500) => new Promise((resolve) => setTimeout(resolve, ms));
type ReducerState = {
count: number;
loading: boolean;
};
const ACTIONS = {
INCREASE: 'increase',
DECREASE: 'decrease',
LOADING: 'loading',
RESET: 'reset',
};
const initialState = {
count: 0,
loading: false,
};
const countReducer = (state: ReducerState, action: { type: string; [key: string]: string }) => {
switch (action.type) {
case ACTIONS.INCREASE:
return { ...state, loading: false, count: state.count + 1 };
case ACTIONS.DECREASE:
return { ...state, loading: false, count: state.count - 1 };
case ACTIONS.LOADING:
return { ...state, loading: true };
case ACTIONS.RESET:
return { ...state, loading: false, count: initialState.count };
default:
return state;
}
}
function UseReducerComponent() {
const [ state, dispatch ] = useReducer(countReducer, initialState);
const { count, loading } = state;
const onIncreaseHandler = async () => {
dispatch({ type: ACTIONS.LOADING });
await sleep();
dispatch({ type: ACTIONS.INCREASE });
};
const onDecreaseHandler = async () => {
dispatch({ type: ACTIONS.LOADING });
await sleep();
dispatch({ type: ACTIONS.DECREASE });
};
const onResetHandler = async () => {
dispatch({ type: ACTIONS.LOADING });
await sleep();
dispatch({ type: ACTIONS.RESET });
};return (
useReducer
Count: {loading ? 'loading...' : count}
+
-
reset
);
}
```### render useCallback Component
↥ back to examples[![Edit creact useCallback demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/creact-usecallback-demo-422lf?fontsize=14)
```tsx
import { useCallback, useState } from "@caiym/react";function useInputValue(initialValue: string) {
const [ value, setValue ] = useState(initialValue);
// stable onChange prop, avoid unnecessary render
const onChange = useCallback((event: MouseEvent) => {
const target = event.target as HTMLInputElement;
setValue(target.value);
}, []);return {
value,
onChange,
};
}
function UseCallbackComponent() {
const name = useInputValue('Jack');return (
useCallback
Value: {name.value}
);
}
```### render useMemo Component
↥ back to examplesIn fact, the example is't very suitable, because re-render of MemoChild component has been avoided by props comparison. For a better example, please look at the example of [render useContext Component](#render-usecontext-component).
[![Edit creact useMemo demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/creact-usememo-demo-r8rwp?fontsize=14)
```tsx
import { useMemo, useState } from "@caiym/react";function MemoChild(props: {
count: number;
}) {
return useMemo(() => (
MemoChild: {+new Date()}
), [props.count]);
}
function UseMemoComponent() {
let [ count, setCount ] = useState(0);
let [ num, setNum ] = useState(0);return (
useMemo
Count: {count}
Number: {num}
setCount(count + 1)}>
setCount to trigger MemoChild re-render
setNum(num + 1)}>
setNum don't trigger MemoChild re-render
);
}
```### render useRef Component
↥ back to examples[![Edit creact useRef demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/creact-useref-demo-955mp?fontsize=14)
```tsx
import { useRef } from "@caiym/react";function UseRefComponent() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};return (
useRef
Focus the input
);
}
```### render useContext Component
↥ back to examples[![Edit creact useContext demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/creact-usecontext-demo-f7bkf?fontsize=14)
```tsx
import { React, useState, useContext } from "@caiym/react";const CounterContext = React.createContext(0);
function ContextChild() {
const count = useContext(CounterContext);
return (
count: {count}
);
}
function ContextMemoChild(props: {
count: number;
}) {
return useMemo(() => (
MemoChild: {+new Date()}
), [props.count]);
}
function CommonChild(props: {
count: number;
}) {
returnCommonChild: {+new Date()};
}function UseContextComponent() {
const [ count, setCount ] = useState(0);
return (
useContext
setCount(count + 1)}>setCount
);
}
```## Refs
Thank you here!
- [react-in-160-lines-of-javascript](https://medium.com/@sweetpalma/gooact-react-in-160-lines-of-javascript-44e0742ad60f).
- [从零开始实现一个React](https://github.com/hujiulong/blog/issues/4).
- [Preact](https://github.com/developit/preact).Welcome to commit [issue](https://github.com/caiyongmin/creact/issues) & [pull request](https://github.com/caiyongmin/creact/pulls) !