striped off from someone else's existing project
re-examine each part and determine how exactly we want it to function
import * as React from 'react';
const useTitleNaive = title => {
React.useEffect(() => {
document.title = title;
}, [title]);
};
export default () => {
useTitleNaive('hello!');
return <>title should be "hello!"</>;
};
Text
import * as React from 'react';
const useTitleNaive = title => {
React.useEffect(() => {
document.title = title;
}, [title]);
};
const Component = ({ title }) => {
useTitleNaive(title);
return <div>component sets the title to {title}</div>;
};
export default () => {
useTitleNaive('page');
return (
<>
page sets title to "page"
<Component title="component" />
</>
);
};
import * as React from 'react';
const Title = () => {
const useTitleNaive = title => {
React.useEffect(() => {
document.title = title;
}, [title]);
};
}
const Component = ({ title }) => {
return <div>
<Title>{title}</Title>
component sets the title to {title}</div>;
};
export default () => {
return (
<>
<Title>Page</Title>
page sets title to "page"
<Component title="component" />
</>
);
};
page 1
page 2
page 3
user
app core
badges
app core
const useBadge = (id, text) => {
const { badges, setBadges } = useContext(BadgeContext);
useEffect(() => {
setBadges({...badges, [id]: text});
}, [id, text])
};
const MyPage = () => {
useBadge('weather', 'raining');
useBadge('notification', '2');
return (<>
<Badge id="weather" />
<Badge id="notification" />
</>);
};
const reducer = (state, action) {
switch (action.type) {
case 'new badge': {
return {
...state,
[action.id]: [action.text]
};
}
default: {
throw new Error();
}
}
}
const MyPage = () => {
const [, dispatch] = useReducer({});
dispatch({type: 'new badge', id: 'weather', text: 'rainy' });
dispatch({type: 'new badge', id: 'notification', text: '2' });
return <>
<Badge id="weather" />
<Badge id="notification" />
</>;
}
some pseudocode
badges
app core
user
badges
app core
user
sider collapse state
current route and params
// core library
const Layout = () => {
useEffect(() => {
return subscribe('debug', ({page}) => {
console.log(`debugging ${page}`)
});
}, []);
return // route-based page loader
};
// page
import { dispatch } from 'outerspace-headquarter';
const MyPage = () => {
dispatch('debug', { page: 'my page' })
return <div />
};
each communication pathway connects an interaction to a callback, but most of the time, the callback sets payload to an internal state
changing part of the store does not change other part... kind of serves as a temporary storage cross the SPA
page 1
page 2
page 3
listing
detail
edit
page 1
page 2
page 3
user
routes
ui states
app core
but it will still flash a white screen when `null` is being rendered
const App = () => (
<main>
<Router>
<Loader path="/cat" key="/cat" />
<Loader path="/dog" key="/dog" />
</Router>
</main>
)
const loaderMap = {
"/cat": () => import("./cat"),
"/dog": () => import("./dog")
};
const Loader = ({
path,
loader,
children
}) => {
const [module, setModule] = React.useState(modules.get(loader));
React.useEffect(() => {
if (!module) {
fetchModule(path, loader).then(mod => {
setModule(mod);
});
}
}, [loader, path, module, setModule]);
if (!module) {
// this _will_ result in a flickering before the 1st time the component is loaded
return null;
}
const { default: Component, ...rest } = module;
return <Component {...rest}>{children}</Component>;
};
const fetchThenNavigate = path => e => {
e.preventDefault();
fetchModule(path).then(() => navigate(path));
};
const fetchModule = async (path, customLoader) => {
if (modules.get(path)) {
console.info(
`%cModule ${path} already loaded`,
`color: #82aaff;${commonLogStyles}`
);
return modules.get(path);
}
const loader = customLoader ? customLoader : window.loaderMap[path];
if (!loader) {
throw new Error(`Didn't see a loader`);
}
console.log(`%cLoading ${path}`, `color: #addb67;${commonLogStyles}`);
return loader()
.then((module: any) => {
console.info(`%c${path} loaded`, `color: #22da6e;${commonLogStyles}`);
modules.set(path, module);
return module;
})
.catch((err: string) => {
console.error(err);
});
};