Skip to content

Commit fc90861

Browse files
committed
feat(app): useStore, more docs in readme, brotli build
1 parent 5dd91b9 commit fc90861

File tree

19 files changed

+530
-88
lines changed

19 files changed

+530
-88
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
example-dev/node_modules
22
examples/todo-app/node_modules
3-
node_modules
3+
node_modules

.npmignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
example-dev/node_modules
22
examples/todo-app/node_modules
3-
node_modules
3+
node_modules
4+
src
5+
example-dev
6+
examples

README.md

Lines changed: 130 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This is a work in progress and calling on all those who feel like modern js fram
2222
- ✅ Create a custom events-driven system for reactive UI updates
2323
- ✅ Create a main Z binding layer to bring everything together
2424
- ✅ Create UI templating and styling foundations
25+
- ✅ Create common templating utilities (like for loops, conditionals, etc.) -- half way there!
2526
- ✅ Handle single page application model and routing
2627
- ✅ Handle automatic re-rendering on state change
2728
- ✅ Add useful hooks and utilities for common patterns
@@ -31,6 +32,8 @@ This is a work in progress and calling on all those who feel like modern js fram
3132
- 🔳 Add builtin low boilerplate data fetching mechanisms
3233
- 🔳 Improve documentation and provide more examples
3334
- 🔳 Introduce Js doc and ship this for massive adoption!
35+
- 🔳 Add tests coverage to ensure more reliability and trust
36+
- 🔳 Do some benchmarks, refactoring and performance optimizations
3437

3538
What else?
3639

@@ -66,7 +69,7 @@ Honestly, you gonna need some tooling to have the best experience, but it's not
6669
This is not a must, but I must tell you that Z Js uses JavaScript template literals even for templating or crafting your UI, for example:
6770

6871
```html
69-
import { html } from 'z.js'
72+
import { html } from 'z-js-framework'
7073

7174
let name = 'Kizz'
7275
let greetElement = html`<h1>Hello there, ${name}</h1>`
@@ -83,7 +86,7 @@ Otherwise, here is an example of how it works in great detail. These are the off
8386
``` js
8487
'use strict';
8588

86-
import { render } from 'z.js';
89+
import { render } from 'z-js-framework';
8790
import About from './pages/about.js';
8891
import Home from './pages/home.js';
8992
import Layout from './pages/layout.js';
@@ -133,10 +136,10 @@ Then, have an index HTML file that can act as the entry point for your applicati
133136

134137
Now, let's have a look at a Z Js component, a simple re-usable button component. It can be found in `example/components/button.js` and here is how it's implemented.
135138

136-
> ✅ Components
139+
> ✅ Components & Styling
137140
138141
```js
139-
import { css, html } from 'z.js';
142+
import { css, html } from 'z-js-framework';
140143

141144
export const Button = (children, setCount) => {
142145
const buttonClass = css`
@@ -174,10 +177,75 @@ Ohh, so you are wondering what's happening? Don't freak, let me explain:
174177

175178
Congratulations! You have just created your first Z Js component. Now, let's use it on a page. Let's see how a home page with a button looks, along with some other concepts, state and routing.
176179

177-
> ✅ State Management
180+
> ✅ Global State Management
181+
182+
Here is how you would manage complex state in your z applications, create a file say store.js and define and export your global states, these you can then import and use elsewhere in your app components, no need to wrap into any providers or contexts. It's that dead simple as illustrated below.
183+
184+
```js
185+
186+
import { createStore } from 'z-js-framework';
187+
188+
export const countStore = createStore(100);
189+
190+
export const authStore = createStore(false);
191+
192+
// there's a lot you can do, channel.getHistor() for example gets the store state history upto 10 previous versions
193+
const { getValue, setValue, subScribe, channel } = createStore({
194+
name: 'z-js-framework',
195+
age: 1,
196+
});
197+
198+
// access store state
199+
console.log(countStore.getValue()); // 100
200+
201+
// run everytime state changes
202+
authStore.subScribe((newState) => {
203+
console.log('auth changed::', newState);
204+
});
205+
```
206+
207+
then in any compoent you can just do something like...
208+
209+
```js
210+
import {
211+
html,
212+
reactive,
213+
useEffect,
214+
useStore,
215+
useRouter,
216+
} from 'z-js-framework';
217+
import { authStore } from '../store.js';
218+
219+
export const AuthComponent = () => {
220+
const [user, setUser] = useStore(authStore);
221+
222+
const router = useRouter();
223+
224+
useEffect(() => {
225+
if (!user) {
226+
router.goTo('/');
227+
}
228+
}, []);
229+
230+
let UI = html`
231+
<div>
232+
<h1>Hello, ${user.userName}</h1>
233+
<button onclick="${() => setUser(null)}">Logout</button>
234+
</div>
235+
`;
236+
237+
return reactive(UI);
238+
};
239+
```
240+
241+
The only thing to note here is we import useStore and pass in the store, it makes the state available to the component, and we can use it as a normal state variable within the component, and we can of course update it efficiently, all state updaets are granular and only affect their respective components. You can learn more about how state is handled and other interesting things you can do, z is powered by [State Radio](https://www.npmjs.com/package/state-radio) wrapped under hood for more simplicity but all state radio features can be accessed otherwise via the exposed state channels.
242+
243+
otherwise let's see in details how state works then on component level...
244+
245+
> ✅ Component Level State Management
178246
179247
```js
180-
import { css, html, useEffect, useState } from 'z.js';
248+
import { css, html, useEffect, useState } from 'z-js-framework';
181249
import { Button } from '../components/button.js';
182250

183251
export default function Home() {
@@ -201,7 +269,7 @@ export default function Home() {
201269
<!-- Button Component Usage -->
202270
<div class="flex-item">${Button('+ Add One', setCount)}</div>
203271
</div>`;
204-
272+
205273
// react to state changes
206274
useEffect(() => {
207275
console.log('count changed::', count.current());
@@ -218,14 +286,27 @@ export default function Home() {
218286
Well, what's happening here? let's try to understand the code above.
219287

220288
1. We again import different stuff from the Z Js framework. These are like hooks or utility functions, each doing a well-defined thing.
289+
221290
2. We import the Button component from the components directory. We can also import other components from other directories; it's just a convention to keep all your components in a components directory. We already saw how such a component is made in previous steps!
291+
222292
3. Since we already know about the HTML and CSS functions, let's look at the new ones here: useState and useEffect. These are much inspired by those of React, but make no mistake—they're quite different in how they work. This is not React!
223-
4. The useState returns the state object and a state setter, e.g. count and setCount. The state object has 2 properties that you can use for now: the value and current. The value is the current value of the state, the current is a function that returns the current value of the state, so you can use it to get the current value of the state, and the setter is a function that takes a new value and updates the state, so you can use it to update the state. The state setter is useful when you want to update the state from a function or you want to update the state from a child component. While the current function on the state object is useful when you want to get the current value of the state from a child component or in a series of component and state lifecycle, basically in useffect use state.current() to access state's current value, not just state.value, you will be good.
224-
5. The useEffect is a function that takes a function and an array of state object dependencies. It's called when the dependencies change, and it's called after the component is rendered, so you can use it to react to state changes. You can also use it to fetch data from an API upon some change of state or do any other side effects, but make no mistake. Unlike react, this one only runs when the state changes. It's not run on render of component. It's like an event listener, which only happens when something happens, say, a change of state in this case. Otherwise, if an empty state dependencies array is provided, the provided function is run only once and for all on component load. Otherwise, it would rerun this function every time any of the provided state-dependent objects change or never if they never change!
293+
294+
4. The useState returns the state object and a state setter, e.g. count and setCount. The state object has 2 properties that you can use for now: the value and current. The value is the current value of the state, the current is a function that returns the current value of the state, so you can use it to get the current value of the state, and the setter is a function that takes a new value and updates the state, so you can use it to update the state.
295+
296+
The state setter is useful when you want to update the state from a function or you want to update the state from a child component. While the current function on the state object is useful when you want to get the current value of the state from a child component or in a series of component and state lifecycle, basically in useffect use state.current() to access state's current value, not just state.value, you will be good.
297+
298+
5. The useEffect is a function that takes a function and an array of state object dependencies. It's called when the dependencies change, and it's called after the component is rendered, so you can use it to react to state changes. You can also use it to fetch data from an API upon some change of state or do any other side effects, but make no mistake. Unlike react, this one only runs when the state changes. It's not run on render of component. It's like an event listener, which only happens when something happens, say, a change of state in this case.
299+
300+
Otherwise, if an empty state dependencies array is provided, the provided function is run only once and for all on component load. Otherwise, it would rerun this function every time any of the provided state-dependent objects change or never if they never change!
301+
225302
6. Notice how we manually select the parts we want to update on state change from our home element and change its inner HTML. This is real DOM manipulation. The framework doesn't handle this for you as of now. You update what you want to update as you see fit, just like you would in vanilla JS. This is just a bit simplified, but not a replacement.
303+
226304
7. Notice how we use state.current() inside the useEffect. This ensures we get the latest value of this state object; otherwise, state.value would be the old value of the state object, which would be the value of the state object at the time the component was rendered.
305+
227306
8. State and state setters can be passed to child components as you see how setCount is being passed to the Button component.
307+
228308
9. Unlike vanilla js literals here we can define our literals and attach events all at once, it's like jsx + template literals = jsx literals kind of, you see we attach the onChange handler on the input, and we do this by directly referencing the handleInput function, under the hood z js will create a real dom element out of this template and attach this as it should be, given in it's there in the component scope, or passed as an argument.
309+
229310
10. All component or page functions in z return the created element, thats how we are able to reuse them and render them in the dom.
230311

231312
Almost that's all of Z as of now. Just one last thing, though...
@@ -234,7 +315,7 @@ This next part is how you link between pages.
234315
> ✅ Navigation
235316
236317
```js
237-
import { html, useRouter } from 'z.js';
318+
import { html, useRouter } from 'z-js-framework';
238319

239320
export default function Layout() {
240321
const { getParam } = useRouter();
@@ -271,7 +352,7 @@ As with many modern frameworks, they are able to automatically re-render the app
271352
> ✅ Reactivity
272353
273354
```js
274-
import { html, reactive, useState } from 'z.js';
355+
import { html, reactive, useState } from 'z-js-framework';
275356

276357
export default function SomeComponent() {
277358
const [userName, setUserName] = useState('Kizz');
@@ -299,7 +380,7 @@ It just takes in the promise or fetch function or any async one and a fallback e
299380
> ✅ Hooks
300381
301382
```js
302-
import { html, useSuspense } from 'z.js';
383+
import { html, useSuspense } from 'z-js-framework';
303384

304385
const fallback = html`<p>Loading... chill for now!</p>`;
305386

@@ -328,6 +409,43 @@ function fetchContent() {
328409
}
329410
```
330411

412+
## 🌲 Rendering Lists
413+
414+
Z Js has a few helpers to help you render lists or array of items. This is very useful when you working with a list of items or iterable data and you rendering them in a restrictive semantic element say a table or that you want to maintain the structure of the elements in dom exactly, i.e if elements a to be exactly direct children of the parent element, most frameworks provide helpers here such as the v-for in vue, etc. Here is how you can go about it in z.
415+
416+
> ✅ Rendering Lists
417+
418+
```js
419+
import { html, useState, List, reactive } from 'z-js-framework';
420+
export default function TodosPage() {
421+
const [todos, setTodos] = useState([
422+
{
423+
id: 1,
424+
task: 'something cool',
425+
completed: true,
426+
},
427+
{
428+
id: 2,
429+
task: 'something again',
430+
completed: false,
431+
},
432+
]);
433+
let UI = () => html`
434+
<table class="todos-table">
435+
<tbody ref="todoRef">
436+
${List({
437+
ref: 'todoRef',
438+
items: todos,
439+
render: ({ item: props }) => TodoItem({...props}),
440+
})}
441+
</tbody>
442+
</table>`
443+
return reactive(UI);
444+
}
445+
```
446+
447+
As you can see, the `List` utility takes in a few options, the ref is the ref of the parent element, items is the array of items to render, and render is the function that renders each item, it should return a single element and takes in each item in the items array as item which you can even alias as props.
448+
331449
That shows loading, and then bingo shows the content.
332450

333451
## 😇 You want more?

design.js

Lines changed: 0 additions & 23 deletions
This file was deleted.

0 commit comments

Comments
 (0)