Im wrecking my brain trying to figure out why this is map is repeating twice.
let toolbars = toolbarState.map(( toolbarEntry, i ) => {
let curToolbar = toolbarState.keyOf( toolbarEntry ); // returns the key of the property
let customProps = {};
switch( curToolbar ){
case( "temSelectionCtrl" ):
case( "functionalCtrl" ):
case( "referenceCtrl" ):
return( // returns react component
<CtrlParent eachToolbar = { toolbarEntry }
svgState = { svgState }
customProps = { customProps }
key = { i }/>
);
case( "operationalCtrl" ):
customProps = {
enableZoomIn,
enableZoomOut,
dispatchEnableZoom
};
return (
<CtrlParent eachToolbar = { toolbarEntry }
svgState = { svgState }
customProps = { customProps }
key = { i }/>
);
}
});
To clarify, Im using immutableJs libary. Which has a method for reading the key of a value.
Also, the structure of the toolbarState looks like this:
toolbarState : {
temSelectionCtrl: {...},
functionalCtrl: {...},
operationalCtrl: {...},
referenceCtrl: {...}
}
Related
Hi i have problem with my react app. I have page with list of Subjects which data i get with react-apollo query. This page has action which links me to another component.
And then in that Child component i have Button Back which when i click it send me back to that lists of views... BUT this time it throws me null pointer error and i don't why it is happened.
const getSubject = `query GetSubject($id: ID!) {
getSubject(id: $id) {
id
name
description
packages(limit:999) {
items {
id
name
description
order
themes(limit:999){
items {
id
name
order
}
}
}
}
}
}
`;
function SubjectView(props) {
const classes = useStyles();
let width = window.innerWidth;
let years = [];
const [rocnikValue, setRocnik] = useState(0);
const [mobile, setMobile] = useState(0);
useEffect(() => {
function changeSize() {
if ((window.innerWidth < 960) && (mobile === false)) {
setMobile(true);
}
else if ((window.innerWidth > 960) && (mobile === true)) {
setMobile(false);
}
else return;
}
window.addEventListener("resize", changeSize.bind(this));
return function cleanup() {
window.removeEventListener("resize", changeSize.bind(this));
};
});
const handleSelect = event => {
setRocnik(event.target.value);
};
return (
<>
<Query
query={gql(getSubject)}
variables={{ id: props.match.params.subjectId }}
>
{result => {
if (result.loading) {
return (
<LinearProgress />
);
}
if (result.error) {
return (
<Typography color="error" variant="body1">
{result.error}
</Typography>
);
}
/* HERE I GET NULL POINTER ERROR */
result.data.getSubject.packages.items
.sort((a, b) => a.order - b.order)
.map((item,i) => years[i] = item.name)
if (!rocnikValue.length) {
setRocnik(years[0]);
return null;
}
if (width < 960) {
if (!mobile.length) setMobile(true);
return (
<div className={classes.page}>
<SubjectHeader
subject = {result.data.getSubject}
years = {years}
handleSelect = {handleSelect}
rocnik = {rocnikValue}
/>
{result.data.getSubject.packages.items
.sort((a, b) => a.order - b.order)
.map((pkg, pkgIndex) => (
<Fragment key={pkgIndex}>
{pkg.name === rocnikValue &&
<MobileView
key = {pkgIndex}
rocnik = {pkg}
/>
}
</Fragment>
))}
</div>
);
}
else {
if (!mobile.length) setMobile(false);
return (
<div className={classes.page}>
<SubjectHeader
subject = {result.data.getSubject}
years = {years}
handleSelect = {handleSelect}
rocnik = {rocnikValue}
/>
<DesktopView
subject = {result.data.getSubject}
rocnik = {rocnikValue}
/>
</div>
);
}
}}
</Query>
</>
);
}
Child component with back button is not important i think.
Anyway why is this happening ?
You have checked variables loading and error. I would also check the data before using that. Something like this:
if (result.data && result.data.hasOwnProperties('getSubject') && result.data.getSubject) {
...insert your actions here
} else {
return null <== If it's Ok...
}
I'm trying to make a search functionality for my app. It works with API:
http://localhost:3005/products?q=[USER INPUT HERE]
and .JSON is returned from this. I already have a working component that I want to duplicate and use it for search results display. It looks like this:
class Item extends Component {
constructor(props) {
super(props);
this.state = {
output: {},
url: {}
}
}
componentDidMount() {
fetch(this.props.url)
.then(response => response.json())
.then(data => this.setState({ output: data }));
}
render() {
const { general = {name:"", description:""} } = this.state.output;
return (
<BoxTitle>{general.name}</BoxTitle>
);
}
}
working alright, rendered this way:
let ChoosePage = (i) => {
ReactDOM.unmountComponentAtNode(document.getElementById('items'))
let urls = [
'http://localhost:3005/products/774944',
'http://localhost:3005/products/774945',
...
'http://localhost:3005/products/738471'];
let urls_sliced = urls;
if (i === 0) {
urls_sliced = urls.slice(0, 4);
} else if (i === 1) {
urls_sliced = urls.slice(4, 8);
} else if (i === 2) {
urls_sliced = urls.slice(-2);
}
let show_items = () => {
ReactDOM.render(urls_sliced.map((url)=>{
return(
<Item url={url}/>
)
}), document.getElementById('items'));
}
show_items()}
this is my input field:
const search_box = (
<form>
<Icon>search</Icon>
<input placeholder={'Search...'}></input>
</form>
);
I'm looking for a way to pass value inputted by the user to function that will convert it to link and use for getting .JSON from API and then render components mapped with this data. Managed to make only this:
let url_s = 'http://localhost:3005/products?q=' + input;
let show_results = () => {
ReactDOM.render(urls_sliced.map((url)=>{
return(
<Item url={url_s}/>
)
}), document.getElementById('items'));
}
show_results()
Help is very appreciated here :)
You're not supposed to put rich data (objects, arrays, functions) in HTML element attributes. Instead, it's suggested to only put rich data in properties (according to the Google custom elements best practices article). I need to run actions when these properties are updated. We have observedAttributes and attributeChangedCallback, but there's nothing similar for properties.
Let's say I have a user prop with things like name, DoB, and address on it. I thought I might be able to trick observedAttributes by putting a bunk setter a la
set user(val) {
return;
}
Didn't work. return this.user = val gives an infinite loop.
My only idea at this point is to have a property called _user that simply gets set to [Object object] on every change, which triggers the change I actually want. Don't really like that though.
UPDATE: This is what I'm currently doing
In user-info.js:
class UserInfo extends HTMLElement {
connectedCallback() {
subscribers.push({ element: this, props: ['user'] });
this._user = state.user;
this.render();
}
static get observedAttributes() {
return ['user'];
}
attributeChangedCallback(name, oldValue, newValue) {
this.render();
}
get user() {
return this._user;
}
set user(val) {
if (JSON.stringify(val) !== JSON.stringify(this._user)) {
this._user = val;
return this.setAttribute('user', val);
}
}
render() {
this.innerHTML = `<span>${this._user.name}</span> was born on <span>${this._user.dob}</span>`;
}
}
In main.js:
document.querySelector('.actions--user').addEventListener('input', e => {
state.user = {...state.user, [e.target.dataset.action]: e.target.value};
})
You can use a Proxy to detect updated properties of an object.
customElements.define( 'user-info', class extends HTMLElement {
connectedCallback() {
this._user = {
name: 'Bruno',
dob: '1/1/2000'
}
this.render();
this._proxy = new Proxy( this._user, {
set: ( obj, prop, val ) => {
if ( prop === 'name' )
if ( this._user.name !== val ) {
console.log( 'username updated to ' + val )
this._user.name = val
this.render()
}
}
} )
}
get user() {
return this._proxy
}
set user(val) {
if (JSON.stringify(val) !== JSON.stringify(this._user)) {
this._user = val
this.render()
}
}
render() {
this.innerHTML = `<span>${this._user.name}</span> was born on <span>${this._user.dob}</span>`
}
} )
<user-info id=ui></user-info><br>
<label>Name: <input oninput="ui.user.name=this.value"></label>
Alternately you could define a User object / class with setters that would interact with the custom element.
class User {
constructor( elem ) {
this._elem = elem
this._name = 'Bruno'
this._dob = '1/1/2000'
}
set name( val ) {
if ( val !== this._name ) {
this._name = val
this._elem.render()
}
return false
}
get name() {
return this._name
}
get dob() {
return this._dob
}
update( obj ) {
this._name = obj.name
this._dob = obj.dob
}
}
class UserInfo extends HTMLElement {
connectedCallback() {
this._user = new User( this )
this.render()
}
get user() {
return this._user
}
set user(val) {
this._user.update( val )
this.render()
}
render() {
this.innerHTML = `<span>${this._user.name}</span> was born on <span>${this._user.dob}</span>`
}
}
customElements.define( 'user-info', UserInfo )
<user-info id=ui></user-info><br>
<label>Name: <input oninput="ui.user.name=this.value"></label>
I want to render the text 'Results!' and the name of the largestLikeResult from my getLargest() function.
getLargest() {
var largestLikeResult = null
var largerstLikeNum= 0
if(this.props.results!=null){
//.map goes through every result
this.props.results.map(i=> {
console.log(i.name)
this.state.resultsRef.child(this.replaceAll("."," ",i.name)).once('value',function(snapshot) {
if(largerstLikeNum<snapshot.val().right)
{
console.log("new largest in town")
largerstLikeNum = snapshot.val().right
largestLikeResult= i.name
console.log(largestLikeResult)
}
})
})
return (
<div>
{largestLikeResult}
</div>
)
}
else {
return null
}
}
render(){
return (
<div>
Results!
<h1>{this.getLargest()}</h1>
</div>
)
}
}
export default DisplayResults
Currently, only Results! shows up on page and the name of the largestLikeResult shows up in the console, not page. Any quick changes I can add to render() to show the value of largestLikeResult?
Thanks in advance!
For a quick change, I think if you change from map to forEach it will work fine:
this.props.results.forEach(i => {
But I'd suggest to refactor getLargest() function into something similar to this:
getLargest() {
let largerstLikeNum = 0;
const { results } = this.props;
const { resultsRef } = this.state;
// Always return early
if (!results || !Array.isArray(results)) {
return null;
}
return (
<div>
{
results.map(i => {
return resultsRef.child(this.replaceAll('.', ' ', i.name)).once('value', snapshot => {
if (largerstLikeNum < snapshot.val().right) {
console.log('new largest in town');
largerstLikeNum = snapshot.val().right ;
return (
<div>{i.name}</div>
);
}
return null;
})
})
}
</div>
);
}
I create a component like so:
let bList = bObj.map((obj, index) => {
let {
icon, htmlType, onClick } = obj;
let _b = <Button
htmlType = { htmlType }
icon = { icon }
onClick = { () => {this._onClick()} } />
if(someVar) {
return (
<AnotherComp style = { someVar }
key = { index } >
{ _b }
</AnotherComp>
);
} else {
return (
{ _b }
);
}
});
bList =
<div style = { wrap }>
<myComp style = { grp }>
{ buttonsList }
</myComp>
</div>
return bList;
That returns me
Uncaught Error: Objects are not valid as a React child (found: object with keys {_bu}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of MyComp.
However, when I write it like this:
let bList = bObj.map((obj, index) => {
let {
icon, htmlType, onClick } = obj;
if(someVar) {
return (
<AnotherComp style = { someVar }
key = { index } >
<Button
htmlType = { htmlType }
icon = { icon }
onClick = { () => {this._onClick()} } />
</AnotherComp>
);
} else {
return (
<Button
htmlType = { htmlType }
icon = { icon }
onClick = { () => {this._onClick()} } />
);
}
});
bList =
<div style = { wrap }>
<MyComp style = { grp }>
{ buttonsList }
</MyComp>
</div>
return bList;
It works. Where is the difference between saving <Button ../> in a variable and writing it in there directly?!
Difference is you are using extra {}, remove that it will work:
return _b;
Meaning of return ({ _b }) is:
return ({'_b' : _b});
That means you are returning an object with key _b, not the JSX.
Check this snippet:
let a = 5;
let b = { a };
console.log('b = ', b);