In what cases can such a structure
useEffect(() => {
...
}, []);
run multiple times?
I thought it shouldn't by definition, but on this video it does:
https://www.youtube.com/watch?v=7RMwZ0_tANg
[] means, that it will render only on initial render, so probably there is multiple rendering of a component
The way you used useEffect it is working as equality to componentDidMount for class component.
useEffect can be used as componentDidMount , componentDidUpdate and componentWillUnmount.
useEffect(() => {
console.log('mounted'); //This way you can get componentDidMount
return () => console.log('unmounting...'); //This way you can get componentDidUnmount
}, []) // <-- The effect depends on variable you put into array(if you would want to check and do something every time variable did update you would put variable name inside - componentDidUpdate)
I found out, how that was possible.
const ComponentWithLoader = WithLoader(Component);
return <ComponentWithLoader { ...{
isLoading, data, remove, update, setData, ...props,
}}/>;
I used a higher-order component inside the body of a functional component. And since WithLoader call created a completely new component every time, the whole component structure inside it was recreated on every rerender of the outer functional component.
Related
With react, in the JSX code returned by a functional component I can have:
MyComponent.jsx
<MySubComponent props1={{a: 0, b:1}}/>
OR
<MySubComponent props2={["toto", "tata"]}/>
Where my sub component is exported with memoization:
MySubComponent.jsx
export default React.memo(MySubComponent)
However this breaks memoization, because props with the format {{...}} or {[...]} will instanciate a new object/array each time (same value, but different memory address), thus props shallow-compare by React.memo sees it different.
Is there any ESLint/JSLint rule to detect those kinds of props ?
You can use useMemo to memoize the object or array and pass it as props to MySubComponent like this:
const obj = React.useMemo(() => ({a: 0, b:1}), []);
<MySubComponent props1={obj}/>
In SubComponent.jsx
export default React.memo(MySubComponent);
This only works for simple objects.
In a parent component I have something like:
render() => {
const data = {a:1,b:[1,2,3]}; // of course this is a simplified version of the code
return html`<child-component data=${data}></child-component>`
}
Which is basically equivalent to:
render() => {
const data = {a:1,b:[1,2,3]}; // of course this is a simplified version of the code
return html`<child-component data="[object Object]"></child-component>`
}
Which is basically useless...
Is there a simple way to pass complex object hierarchies into litElement components?
As far as I can tell, my options are:
Option 1. Use attributes: I'm a bit of a litElement noob so I'm not sure if this will work and I'm not sure how to make it work without having to make extra function calls. It would be nice if I could just do all the necessary work inside html.
Research in progress.
Option 2. Use Json.
Stringify the object in the parent component
render() => {
const data = {a:1,b:[1,2,3]}; // of course this is a simplified version of the code
return html`<child-component data=${JSON.stringify(data)}></child-component>`
}
then parse the json in the child component.
This just seems a bit inelegant to me though.
But it works.
In this case what you probably want is to pass the object as a property rather than as an attribute. For complex data such as objects, arrays, functions, etc. that's the preferred method.
You can do it with the following syntax:
render() => {
const data = {a:1,b:[1,2,3]};
// note the period (.), that's the token used to identify that you're passing data as a property
return html`<child-component .data=${data}></child-component>`
}
In general, you should probably give Lit's templating guide a read as some of the most common use cases are covered throughout it.
The lit-element documentation describes conditional rendering via (condition ? a : b). I was wondering how to use that to render one of multiple pages, f.e. in combination with mwc-tab-bar from Googles material web components.
My current solution is something like this:
render() {
... other stuff ...
${this.selectedPage === 0 ? html`
<div>
...
</div>
` : html``}
${this.selectedPage === 1 ? html`
<div>
...
</div>
` : html``}
... further pages ...
}
I don't like the :html`` part but is that how it's meant to be?
Use more simple code like this.
constructor(){
super();
// don't forget add `prop` and `selectedPage` to `static get properties()`
this.prop = 1;
}
render() {
return this.getPage(this.selectedPage);
}
getPage(num){
switch(num){
default:
case 1:
return html`<div>P${this.prop}<div>`;
case 2:
return html`<div>P2<div>`;
}
}
There are multiple ways of achieving this, your solution is one, but as you mention, it's not the prettiest
One way you could modularize this somewhat is using an object/array and render functions, basically the idea is this:
First, define render functions for each page (this can be on the same file or on different files):
const page0Renderer = (context) => {
return html`<section>${context.someData}</section>`;
};
Then, you could define an object that has a match between the page identifiers and their respective functions, you are using numbers so the sample below uses numbers:
const pageRenderers = {
'0': page0Renderer,
'1': page1Renderer,
'2': page2Renderer,
// etc
};
And in your main render function you could use all these like this:
render() {
return html`
${pageRenderers[`${this.selectedPage}`](this)}
`;
}
This would basically call the render function that matches the selected page and send it a reference to the main web component so that you can access its properties.
Then again, this approach also has its flaws and I wouldn't really recommend it much if you need your child templates to be complex.
In that case, instead of rendering functions you probably would be better off creating other components for each view and that way you could also do some lazy loading and so on.
For that kind of approach, you might want to check out routers like vaadin router which help you both with routing and changing which component gets displayed accordingly
I'm trying to test a component that is wrapped in Redux:
beforeEach(async () => {
component = await mount(
<Provider store={buildStore()}>
<CheckoutOverlay cartItems={basicCart} />
</Provider>
)
await component.update();
})
Things I've tried:
component.find(CheckoutOverlay).instance().state.coupon={} (Cannot read 'state' of null)
component.find(CheckoutOverlay).state.coupon={mode:0, value:.25}; (state does not update)
component.find(CheckoutOverlay).setState({coupon: {mode: 0, value: .25}}); (setState() can
only be called on class components)
component.find(CheckoutOverlay).instance().setState({coupon: {mode: 0, value: .25}}); (cannot read property 'setState' of null)
I've also tried some suggestions that use SHALLOW() and .dive(). But I always get the error 'dive()' is not a function. I am using Enzyme 3.11.0
How can I update the state of a component wrapped in the Redux Provider?
Additional Info:
Inside my component, CheckoutOverlay, I have another component that calls a function in CheckoutOverlay. This function stores the data sent in the State.
I am trying to simulate what happens when the data is sent to CheckoutOverlay.
The sub component has tests, and, I suppose, I can do something like simulate the click on that component. But that seems like it is more involved than it should be.
Since you have not mentioned what you are trying to test, I am giving you some general suggestions.
You should not test the implementation details, rather test the functionality.
If your state is dependant on the redux store state, you can pass the required data as initialState to your provider.
You cannot use instance on a functional component
In the feathersjs docs the explanation provided is as follows:
pluck discards all fields except for the specified ones, either from
the data submitted or from the result. If the data is an array or a
paginated find result the hook will remove the field(s) for every
item.
import _pluck from '../common/_pluck';
import checkContextIf from './check-context-if';
import getItems from './get-items';
import replaceItems from './replace-items';
export default function (...fieldNames) {
return context => {
checkContextIf(context, 'before', ['create', 'update', 'patch'], 'pluck');
if(context.params.provider) {
replaceItems(context, _pluck(getItems(context), fieldNames));
}
return context;
};
}
The getItems utility returns the items in either hook.data or
hook.result depending on whether the hook is being used as a before or
after hook. hook.result.data or hook.result is returned for a find
method.
The returned items are always an array to simplify further processing.
The replaceItems utility is the reverse of getItems , returning the
items where they came from.
My question relates to the checkContextIf function. This function prevents the pluck hook from being called except before the create,update and patch methods. How then does the pluck hook work on the results of the query. Are not the results produced after the service call and handled in an after hook?
As the documentation states:
The getItems utility returns the items in either hook.data or hook.result depending on whether the hook is being used as a before or after hook.
hook.data is the data (body) sent with a create, patch or update request so it can be used to omit fields that you do not want to be saved to the database. This is also documented in the hooks API:
data - The request data (for create, update and patch)