Difference between `createContext()` and `createSignal()` for global state - solid-js

In solid-js, what is the benefit of using context, when we can create a signal and import it's getter/setter/even a memoized value from a store global store-like file?:
// ./store/myValue.js
export const [value, setValue] = createSignal('');
To my understanding it is totally ok to import this value in multiple components and the reactivity would be maintained throughout those.
The only use-case of the store/global that I can think of, is if your state grows to such a complexity, that you need a more structured context-like approach to improve maintainability, readability etc.

Context in Solid is a scope that wraps its child scopes. The main benefit of context is you can re-write a context value at different levels of the component tree. It other words, you can have different values in different contexts, all at the same time.
Check this example below. We can switch theme as you go deeper into the component tree. A global store will give you the same value at every level.
const ThemeContext = createContext<string>('black');
const Child = () => {
const theme = useContext(ThemeContext);
return (
<div style={{ color: theme }}>Child Component</div>
);
};
const App = () => {
return (
<div>
<Child />
<ThemeContext.Provider value='red'>
<Child />
<ThemeContext.Provider value='blue'>
<Child />
</ThemeContext.Provider>
</ThemeContext.Provider>
</div>
);
}
render(App, document.querySelector('#app'));
This will output:
<div>
<div style="color: black;">Child Component</div>
<div style="color: red;">Child Component</div>
<div style="color: blue;">Child Component</div>
<div style="color: green;">Child Component</div>
</div>

The benefit of context is the ability to bind a state to your component tree. Imagine you have a radio button group that you want to reuse. A global state would mean you could only ever use it once in your page, lest the next usage will overwrite the previous one - tying the state to the component with a context will make this task a lot easier.

Related

How to sync form and useState object?

How do I link a graph using a state to a form, so when the form changes the graph is automatically updated as well, even the form is not submitted yet? The graph is just using showCarrot to generate a chart.js element.
I found initialValues as fields for forms but do not know how to clue this all together. Anyone can help to save me from insanity?
Update: I figured that Ant Design form is based on field-form, still not know how this helps but it is a trail.
That's the state I set and propagate to my form and my graph
const [showCarrot, setCarrot] = useState<ICarrotArray>([]);
That's my form and table to edit the data
<Form form={form} initialValues={showCarrot} onFinish={onFinish}>
<Table
dataSource={showCarrot}
rowKey={"id"}
pagination={false}
bordered
footer={(): React.ReactElement => {
return (
<Button onClick={addCarrot}>
<PlusOutlined /> Add Carrot
</Button>
);
}}
>
...
</Table>
<br />
<Row justify="end">
<Form.Item>
<Button type="default" onClick={onReset}>
Restore
</Button>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" style={{ marginLeft: 8 }}>
Save
</Button>
</Form.Item>
</Row>
</Form>
Firstly I just want to understand, correct me if I'm wrong.
You set your initialValues for form which is showCarrot. Is it mean that initialValues set for some Form.Item, because usually object element inside initialValue is used as data for Form.Item by their name.
Like so
<Form
name="validate_other"
{...formItemLayout}
onFinish={onFinish}
initialValues={{
['input-number']: 3, //name of the Form.Item
['checkbox-group']: ['A', 'B'],
rate: 3.5,
}}
>
<Form.Item label="InputNumber">
<Form.Item name="input-number" noStyle>
<InputNumber min={1} max={10} />
</Form.Item>
<span className="ant-form-text"> machines</span>
</Form.Item>
But by your code it look you want set showCarrot only as table data. If so, I can't find columns in your table, look like currently error because of that. You should add there dataIndex param which will listen for your data elements by that name

material-ui Snackbar component error : <div> cannot appear as a descendant of <p>

I'm using a very simple sample of the material ui Snackbar component, that renders the following HTML
<div class="MuiSnackbar-root-352 MuiSnackbar-anchorOriginBottomLeft-358">
<p class="MuiTypography-root-199 MuiTypography-body2-207 MuiPaper-root-121 MuiPaper-elevation6-129 MuiSnackbarContent-root-373" role="alertdialog" aria-describedby="message-id" direction="up" style="transform: translate(0px, 0px); transition: transform 225ms cubic-bezier(0, 0, 0.2, 1) 0ms;">
<div class="MuiSnackbarContent-message-374">
<span id="message-id">TunisiaNet created!</span>
</div>
</p>
</div>
for the following code
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={this.state.entityGroupAdded}
autoHideDuration={4000}
onClose={this.handleClose}
ContentProps={{
'aria-describedby': 'message-id',
}}
message={<span id="message-id">{this.state.name} created!</span>}
/>
This naturally leads to an HTML specification incompatibility. The issue is that I don't know how to tell the Snackbar to create a div instead of a p element
I get the error:
0.chunk.js:101009 Warning: validateDOMNesting(...): <div> cannot appear as a descendant of <p>.
in div (created by SnackbarContent)
in p (created by Typography)
in Typography (created by WithStyles(Typography))
in WithStyles(Typography) (created by Paper)
in Paper (created by WithStyles(Paper))
in WithStyles(Paper) (created by SnackbarContent)
in SnackbarContent (created by WithStyles(SnackbarContent))
Might wanna check your Material UI version or any override settings you might have made which includes typography components. I just tested a snackbar with the exact same code and the p tag (which uses typography, this part is causing the error) does not exist as a p tag but a different classname
<div class="MuiSnackbar-root-185 MuiSnackbar-anchorOriginBottomLeft-191">
<div class="MuiTypography-root-313 MuiTypography-body1-322 MuiPaper-root-20 MuiPaper-elevation6-28 MuiSnackbarContent-root-310" role="alertdialog" aria-describedby="message-id" style="transform: translate(0px, 0px); transition: transform 225ms cubic-bezier(0, 0, 0.2, 1) 0ms;">
<div class="MuiSnackbarContent-message-311">
<span id="message-id">Note archived</span></div>
</div>
For other who is looking for an answer other than something wrong with the overrides, most likely if nothing wrong with your overrides, and even before you check for that. Look into your code if it contain a component that inherit the props of Typography component like DialogContentText and you also have another Typography component or a component that will transfer to <p> tag in HTML as a child of that component.
In the the error above message props will become
<p class="MuiTypography-root-199 MuiTypography-body2-207 MuiPaper-root-121 MuiPaper-elevation6-129 MuiSnackbarContent-root-373" role="alertdialog" aria-describedby="message-id" direction="up" style="transform: translate(0px, 0px); transition: transform 225ms cubic-bezier(0, 0, 0.2, 1) 0ms;">
<div class="MuiSnackbarContent-message-374">
<span id="message-id">TunisiaNet created!</span>
</div>
</p>
I think this is not showing anymore in the new versions of material. But if you want to fix something like that take a look;
This will give errors
const someFunction= (): JSX.Element => {
return (
<DialogTitle>
Some title
</DialogTitle>
<DialogContentText>
<Typography variant='h5'>
Sometext
<Typography />
</DialogContentText>
<TextField
autoComplete='off'
margin='dense'
variant="outlined"
id='name'
label='email'
type='email'
fullWidth
/>
);
};
this will fix it
const someFunction= (): JSX.Element => {
return (
<DialogTitle>
Some title
</DialogTitle>
<DialogContentText variant='h5'>
Sometext
</DialogContentText>
<TextField
autoComplete='off'
margin='dense'
variant="outlined"
id='name'
label='email'
type='email'
fullWidth
/>
);
};
In this case for example you can use the normal props of Typography with DialogContentText.
Also you can user component='div' as prop of Typography, it could fix it, I didn't test

jQuery selector for .parent().parent()

Given a series of a form's Label and Input elements like:
<div class="labelEditwrap">
<div class="editor-label">
<label for="Address">Address</label>
</div>
<div class="editor-field">
<input class="text-box single-line" id="Address" name="Address" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="Address"></span>
</div>
</div>
I'm trying to select the outer most div when the textbox gets focus so I can highlight both label and input:
$("input").focus(function () {
$(this).parent().parent().addClass("curFocus")
});
I've tried a few combinations including:
$(this).parent().parent() // seems the most obvious
$(this).parent().parents("div:first")
Another question here asking about .parent().parent() was solved by finding a syntax error unrelated to the selector. However, in this case, I can see my hightlighter class if I go up only one parent level (only highlights the editor's div) and also if I climb 3 levels (highlights the container holding the full form).
thx
OK....its not the selector. All the suggested alternates (and the original) are correctly 'selecting' the outside wrapper div. The problem was the CSS and how Floats are being applied to the Label and Editor divs. This CSS will produce correct highlighting and also let the label/editor fields align themselves correctly. [whew]
Up to you guys the best way to close/edit/retitle the question in hopes of helping other avoid my 4 hour toubleshooting ordeal.
-highly appreciate the time taken-
Possible Solutions:-
$('.text-box').live('focus', function(){
$(this).parent().parent().css('border', '1px solid red');
});
$('.text-box').live('blur', function(){
$(this).parent().parent().css('border', 'none');
});
or
$('.text-box').bind('focus', function(){
$(this).parent().parent().css('border', '1px solid red');
});
$('.text-box').bind('blur', function(){
$(this).parent().parent().css('border', 'none');
});
The solution you suggested should work correctly
$(this).parent().parent();
I think the issue here is that your event is being bound before there is an object to bind it to. Have you bound your function on document ready?
Something like:
$(function(){
$("input").focus(function () {
$(this).parent().parent().addClass("curFocus")
});
});
Otherwise using 'live' or 'on' to bind the event will work dynamically.
so like:
$('input').live('focus', function(){
$(this).parent().parent().addClass("curFocus");
});

Persisting JSF2 Composite Component value

I have a datatable which loops through a List and has a value column which renders as such:
<h:dataTable var="assessmentFieldValue" value="#{assessmentBean.assessmentFieldValues}">
...
<ui:fragment rendered="#{assessmentFieldValue.field.type eq 'TEXT'}">
<h:inputText value="#{assessmentFieldValue.value}" />
</ui:fragment>
<ui:fragment rendered="#{assessmentFieldValue.field.type eq 'SLIDER'}">
<component:slider value="#{assessmentFieldValue.value}" />
</ui:fragment>
...
</h:dataTable>
So sometimes I get a standard inputText, sometimes I get my composite slider component:
<composite:interface>
<composite:attribute name="value" />
</composite:interface>
<composite:implementation>
<script type="text/ecmascript">
function updateValue(value) {
$('##{cc.id} span').each(function() {
$(this).text(value);
});
}
</script>
<div id="#{cc.id}">
<input id="sliderComponent" type="range" min="1" max="10"
value="#{cc.attrs.value}"
style="float: left"
onchange="javascript:updateValue(this.value);" />
<h:outputText id="fieldValue" value="#{cc.attrs.value}"
style="min-width: 20px; display: block; float: left" />
</div>
</composite:implementation>
This all renders as I would like it too (deployed on JBoss-AS7), but when I hit the save button associated with the datatable the assessmentBean.assessmentFieldValue List doesn't reflect any value changes on the slider component yet it does for the standard inputText.
Can anyone suggest why changes to the slider aren't making it back to the backing bean? Thanks!
That's because you used a plain HTML <input> element instead of a fullworthy JSF input component like <h:inputText>. This way the value is in no way bound to the JSF context. It's only treated as plain vanilla template output value, not as input value. A composite component isn't going to solve this. You really need to create a fullworthy UIComponent for this (or to look for a 3rd party component library which has already done the nasty job for you, such as PrimeFaces with its <p:slider>).
See also:
When to use <ui:include>, tag files, composite components and/or custom components?

jQuery next() selector when divs are not neighboring

Ive been using the following to change the width of the div.my-div that appears after the one you've clicked:
$(".my-div").click(function () {
$(this).next().css({'width':'500px'});
});
As my divs were neighboring, this worked fine:
<div class="my-div">stuff</div>
<div class="my-div">stuff</div>
<div class="my-div">stuff</div>
However now the structure has changed so they are no longer neighboring:
<div>
<div class="my-div">stuff</div>
</div>
<div>
<div>
<div class="my-div">stuff</div>
</div>
</div>
<div class="my-div">stuff</div>
Whats the simplest way to select the next element of the same class?
Thanks
jQuery will return elements in order of their appearance in the DOM.
As such, you could cache all the .my-div elements, use the index()[docs] method to get the index of the one that received the event, increment it and use the eq()[docs] method to get the next one.
var divs = $(".my-div"); // cache all of them
divs.click(function () {
var idx = divs.index( this ); // get the index in the set of the current one
divs.eq( idx + 1 ).css({'width':'500px'}); // get the one at the next index
});
This saves you from doing a bunch of unnecessary DOM selection and traversing.
Example: http://jsfiddle.net/VrATm/1/
EDIT: Posted wrong example link. Fixed.
You can traverse the tree hierarchy. That is, you can first jump to parent, then to next, then to children, like this:
$(this).parent().next().find(' > div').css({'width':'500px'});