Function component: how to use props as slot like custom component?
Dan-Do opened this issue ยท 27 comments
Hi @tbranyen,
Here is my pseudo-code:
function MyComponent(props) {
return html`
<div class="modal-dialog">
<h2 class="modal-title">${props.title}</h2>
<div class="modal-footer">${props.footer}</div>
</div>
`;
}
innerHTML(document.body, html`<${MyComponent} title="Test Modal Dialog" footer="<button class='btn-close>Close</button><button class='btn-cancel' onClick=${() => this.remove()}>" />`);
The footer props is dynamic. Is it possible without using web-component
?
You should be able to pass like:
<${MyComponent}
title="Test Modal Dialog"
footer=${html`<button class='btn-close>Close</button><button class='btn-cancel' onClick=${() => this.remove()}>Cancel</button>`}
/>
Live demo here: https://glitch.com/edit/#!/handy-silky-fortnight
@Dan-Do that will be achieved with the createSideEffect
method. Stateful function components are still in progress, but I can add this for you. Are there any other React-like function component features you'd like to see added?
Also to be clear this will only map componentDidMount
as componentWillMount
is only available in class components.
I'm thinking of using the following signature:
import { html } from 'https://diffhtml.org/core';
import { createSideEffect } from 'https://diffhtml.org/components';
function Component() {
// componentWillUpdate stuff happens in the render function
createSideEffect(() => {
// componentDidMount/Update
return () => {
// componentDidUnmount
};
// I hesitate to add this array of dependencies to skip re-render, but it is handy.
}, [someVariable]);
return html`Some component markup`;
}
@Dan-Do that will be achieved with the
createSideEffect
method. Stateful function components are still in progress, but I can add this for you. Are there any other React-like function component features you'd like to see added?
Hi @tbranyen I am new to SPA, and I haven't looked at React so I don't know it's features.
For this small request, I just want to fetch data using async/await
then render, then attach to the DOM. I tested the following code but it repeat forever:
function MyComponent(props) {
const getData = function() {//fetch here}
//call getData before return the html
getData(...)
return html`
<div>show ${data} here</div>
<button onclick=${getData(...)}> Refresh </button>
`;//pseudo code just to explain my idea
}
I will try createSideEffect
and get back later.
It seems the version of unpkg.com is different from the master brand. This line is currently in unpkg, which upgrades function to class component
but master brand is
renderedTree = createTree(Component(props));
I am using ES6 module in browser
import { createSideEffect } from 'https://unpkg.com/diffhtml-components?module';
Error: Uncaught SyntaxError: import not found: createSideEffect
Hi @Dan-Do I have not yet added the createSideEffect
method. I will put it in today, I think that will suffice for your needs.
@Dan-Do added in 1.0.0-beta.24: https://diffhtml.org/components.html#create-side-effect
@tbranyen I setup a codepen to test
https://codepen.io/ali33yukisakura/pen/JjOodXK?editors=1011
function View1(props) {...}
But it doesn't work. May be I used it wrong way :(
Checking out your code ๐, I can see a few issues:
The major one is that I didn't add a unit test to ensure both createState
and createSideEffect
work together. There was a bug which I have fixed and will release a new version.
I also noticed that you were using response.text()
instead of response.json()
, I have fixed that and cleaned up the component below. I tested this works with my latest fixes. Once I publish the new version and you make the response.json()
fix, your code should work as expected.
function View1(props) {
const [state, setState] = createState({ isLoading: false, posts: [] });
createSideEffect(async () => {
setState({ ...state, isLoading: true });
const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: 'GET',
credentials: 'same-origin',
headers: {'Accept': 'application/json, text/javascript'},
});
setState({ ...state, isLoading: false, posts: await response.json() });
});
return html`
<div class="view">
<h2>${props.title}</h2>
${state.isLoading
? html`loading...`
: html`${state.posts.map((post) => html`<h3>${post.id} - ${post.title}</h3>`)}`
}
</div>
`;
}
New version 1.0.0-beta.26 contains the fix for the aforementioned bug.
New version 1.0.0-beta.26 contains the fix for the aforementioned bug.
Thanks @tbranyen it works nice. I will come back if finding any issue.
Hi @tbranyen Just notice that you released version beta-27
which made it not work anymore.
I added more html to the codepen, link
https://codepen.io/ali33yukisakura/pen/ZEvXNeZ?editors=1011
I can see a bug in the code that prevents deeply nested components from getting mount/unmount lifecycle events. I've updated the unit tests and will have a fix out by this weekend. Thanks for your patience, this is really helpful having real usage with the components layer.
@Dan-Do I've refactored how lifecycle events are fired and it appears way more stable now since I cache exact component instances to trigger mount on vs crawling to find them.
When I update your codepen to use https://diffhtml.org/components instead of unpkg it works great. Can you try this for a bit and let me know what you think? Once you're happy with it I'll promote out a new version and you can go back to using unpkg.
@tbranyen when I use
import { createState, createSideEffect } from 'https://diffhtml.org/components';
it said
SyntaxError: import not found: createSideEffect
Edit: this error does not happen on my localhost, just on codepen
https://codepen.io/ali33yukisakura/pen/ZEvXNeZ?editors=1011
Edit2: on my localhost, the createSideEffect
does not work.
@Dan-Do probably a cache related issue. Can you fully wipe the browser cache or test your codepen in incognito mode?
Hi @tbranyen It works now.
However I had an issue on my localhost but I cannot reproduce it on codepen.
I have a ViewMain
function ViewMain(props) {
return html`
<div id="view-main" class=${activeRoute === props.route ? "active" : "hidden"}>
<${CompForeignerStatistic} route="/foreigner/statistic" />
<div class="modal fade"></div>
</div>`;
}
The CompForeignerStatistic
uses createSideEffect we discuss so far.
It messed when rendered
<div id="view-main" class="modal fade" route="/foreigner/statistic" div=""></div>
If I remove this line <div class="modal fade"></div>
then it's rendered ok.
Do you have any idea where to investigate?
Looks like something weird with the HTML parser, where it's treating the adjacent div like attributes. Will see if I can reproduce the issue.
I made a reduced test case and can't reproduce there either:
<main id="main"></main>
<script type="module">
import { html, innerHTML } from 'https://diffhtml.org/core';
import { createSideEffect} from 'https://diffhtml.org/components';
const activeRoute = 'test';
function CompForeignerStatistic() {
createSideEffect(() => {
console.log('mounted');
});
return html`<div>testing</div>`;
}
function ViewMain(props) {
return html`
<div id="view-main" class=${activeRoute === props.route ? "active" : "hidden"}>
<${CompForeignerStatistic} route="/foreigner/statistic" />
<div class="modal fade"></div>
</div>`;
}
innerHTML(main, html`<${ViewMain} route="test" />`);
</script>
@tbranyen I know what makes that error.
Please visit the codepen, change this
function View1(props) {
return html`
<div id="view-main" class=${activeRoute===props.route?"active":"hidden"}>
<${Test} fake="true" />
<div class="success">Hello ๐จ OK!</div>
</div>`;
}
by removing the line break in html like this:
function View1(props) {
return html`
<div id="view-main" class=${activeRoute===props.route?"active":"hidden"}>
<${Test} fake="true" /><div class="success">Hello ๐จ OK!</div>
</div>`;
}
Then the <div class="success">Hello ๐จ OK!</div>
won't be rendered.
Awesome, I can reproduce this bug. I will put in a fix asap.
So the reason this is happening is that I added support for special HTML characters <>/
inside attribute values, which is messing up the parsing. I've got a solution locally that works excellent, by first separating out the individual tags, and then parsing the internals.
I think it'll be ready today. So for now you could break up your markup to separate lines to avoid any bugs, and once I get this fix in, it should be working as expected with single lines.