Svelte #each not looping through data - javascript

i'm taking SveleteJS for a test drive and got stuck
Made a Dashboard component and inside that component, i placed a Whiteboard component:
<script>
import Whiteboard from "./Whiteboard.svelte";
export let name;
</script>
<div class="box part-of-dashboard">
<Whiteboard lines={[]} />
</div>
Whitebord.svelte:
<script>
export let lines = [];
export function addLine() {
lines.push("blah");
console.log(lines);
}
</script>
<div style="background-color:red">
{#each lines as line}
<div>
{line}
</div>
{/each}
</div>
<div>
<button on:click={addLine}>
Add Line
</button>
</div>
When i click the button, the console.log triggers and i can see the lines increasing in size, but i don't see it being rendered on the page, just the empty red div wrapping it.
I've tried adding $: to various places, but i'm not yet sure where it should be used and where it shouldn't be used, not that it was making a difference.
How do i get that #each to render a list of divs (and also, what is the correct way to pass in data from the on:click, doing {addLine('blah')} executes that addLine on page load)?

So, this kind of goes against what one might expect, but Svelte does not detect [].push() as mutating the state of the array variable. This is why the console log shows the variable being updated, but Svelte's rendering does not respond.
After doing a little digging, I found several threads (1, 2) that point this out, and clarify that this is true for calling methods on any object.
EDIT: This is also now called out explicitly in the docs / tutorials - see "Updating Arrays and Objects"
One of the easiest solutions (per the linked threads and docs) is to simply re-assign the variable value to itself; Svelte will pick that up and re-render. So, in your scenario, all I had to do to get your code to work was replace this:
export function addLine() {
lines.push("blah");
console.log(lines);
}
With:
export function addLine() {
lines.push("blah");
lines = lines;
console.log(lines);
}

Related

How can I add a react Component To the DOM with jquery?

This is prob a super easy question but, I want to add components to a grid with react and jquery.
gridGame is a black 100px by 100px square and I want to add items into it. Im using rows (a variable) witch is a number from 1-9 to and sumbing it in to the gridGame-${rows} as seen here,so it can auto update the correct row to join. value should be: gridGame-${row} (what ever number row is from 1-9) and then I want to add a component inside gridGame called <Test /.> (witch is declared up-top in unseen parts of the code).
The function below has jquery that I thought would work in this situation:
function = (rows) => {
console.log(`joining ${rows}`)
let value= $(`#gridGame-${rows}`);
value.append("<Test />");
value.css("background-color", 'brown');
$(".create-coinflip-box").css("display", "none");
}
The css background change works but the value.append does not display the react component.
Here is the React Component inside the <Test /.>:
import React from 'react';
import StatusIcon from './img/image.png';
import Player1Icon from './img/image.png'
class newGame extends React.Component {
render() {
return(
<div>
HELLO
</div>
);
}
}
export default newGame;
I honestly have 0 idea on how this doesn't work.
Thanks for the help :)
This answer was from David in the comments. The problem for me was I was trying to render a component through a button and not the actual render its self.
The solution is to instead make the button change the state witch Updates the React DOM. Then just have a if statement that checks the state then display the component.
His comment explains much better and fixed the problem for me.

How can i make a simple counter in React without using state

I'm trying to make a simple app that counts the number of times the button has been clicked. I read this is a good first question in JS interviews.
"Ask the candidate to build a click counter using any popular framework (React preferred in 2020). This ridiculously simple app has one job: keep track of how many times the user has clicked the button during the current session. No storage. No network I/O. Just count clicks. It is intentionally ridiculously simple"
My first instinct would be to use hooks, but the question wants the programmer to do it without using storage which I believe includes using state.
import React from 'react';
import './App.css';
function App() {
let count=0;
const add=()=>{
debugger;
count++;
}
return (
<header className="App-header">
<button onClick={()=>add()}>click me</button>
{count}
</header>
);
}
export default App;
Here is what I tried. Using debugger I can see count does go up but the changes aren't showing on the page. Any advice would be appreciated.
React is a JS library so we can use pure js:
function App() {
return (
<div className="App">
<h1 id="c">0</h1>
<button
onClick={() => {
let c = document.getElementById("c");
c.innerHTML = parseInt(c.textContent)+1;
}}
>
+
</button>
</div>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
The reason your code doesn't produce the expected result is that React doesn't update the dom unless it sees that an update is necessary, i.e., because the state of the component changed.
So, in your case, the component only gets rendered once, when the value of count is 0. If you want to React to re-render the component and show you the new value, you'll have to trigger that with a state change. Alternatively you can follow cybercoder's solution, which actually opts out of React by supplying its own innerHTML.

How to better toggle between view and edit mode in reactjs

I am developing a user profile page, which has many boxes with two modes each - view and edit. Each box is defined as a react class <ProfileBox> ... </ProfileBox>.
For the view and edit mode of each box I defined classes <EditMode> ... </EditMode> and <ViewMode>...</ViewMode>. Eventually I want to render e.g. an address like
<ProfileBox editMode={true}>
<EditMode>
...
</EditMode>
<ViewMode>
...
</ViewMode>
</ProfileBox>
I want the logic to be in the ProfileBox. My current approach is to iterate all children and filter them if they are of type ViewMode or EditMode. Too late, I realized that this breaks down as soon as I do something like:
<ProfileBox editMode={true}>
<EditMode>
...
</EditMode>
<div>
<ViewMode>
...
</ViewMode>
</div>
</ProfileBox>
How could I do it better? What is the standard approach? I don't want to have to manually care about passing an id or a status to the Edit and ViewMode in the definition of the address.
Thank you!
You can continue to render ViewMode and EditMode as children rather than pass them as props by using logic in the render function of ProfileBox similar to this:
render: function() {
var child = this.props.editMode ?
<EditMode>
...
</EditMode> :
<ViewMode>
...
</ViewMode>;
return child;
}
You can do:
<ProfileBox editMode={this.state.editingWhatever} onModeChange={this.updateEditingWhatever}
editView={ <EditMode>...</EditMode> }
viewView={ <div><ViewMode>...</ViewMode></div> }
/>
Or you can conditionally render the EditMode/ViewMode in this code. To make it less ugly, a well designed mixin would do wonders. It's hard to tell what the exact requirements are from your question, but take a look at what all of your <ProfileBox/> uses have in common, and where they differ.
In the simpler case shown above, you probably just want to dynamically create the onModeChange handler, and the onChange handler for any inputs children in edit mode.

polymer - how to get next corresponding element in a repeating template?

sorry for the sort of specific question. I'm trying to make an accordion using core-collapse and repeating templates and am facing some difficulties. Here is my accordion repeating template:
<template repeat="{{item, i in items}}">
<div class="accordheader" on-click="{{toggle}}">{{item}}</div>
<template repeat="{{content, i in contents}}">
<core-collapse class="collapse">
<p>{{content}}</p>
</core-collapse>
</template>
</template>
and here is my script:
toggle: function () {
//get whichever 'accordheader' clicked on
var collapseGetting = this.shadowRoot.querySelector('.accordheader');
console.log(collapseGetting);
//find the core-collapse that is directly underneath it
var collapse = $(collapseGetting).next('core-collapse');
console.log(collapse);
//toggle that particular core-collapse
collapse.toggle();
console.log('toggled');
}
And now my toggle is entirely broken and won't actually toggle anything. I still receive the 'toggled' console log but nothing is happening. I'm not sure if I'm targeting the next core-collapse correctly, or even nesting the repeating templates correctly. Basically I have 2 arrays, [items] and [contents] which I am retrieving my item for each .accordheader and my content for each core-collapse.
Any insight? I feel like this is a formatting or template issue..
Having a quick peek at the docs here:
http://www.w3schools.com/jquery/traversing_next.asp
(See the 'Try It Yourself' example')
I believe you're current code is getting the next '.accordheader' element instead of the next 'core-collapse'.
If I've interpreted the link properly then it should look something like this:
$('core-collapse').Next( console.log(collapse); );
Never used 'Next' before, but there's my 5 cents.

Reactjs append an element instead of replacing

I'm trying to iterate through a list/array/object of things: (I used coffeescript to keep it clear, jsfiddle of full JS here. but it's just a forEach)
pages = for page, each of #props.ids
$('#start').append("<div id='"+page+"' ></div>")
React.renderComponent page(title: each.title, text: each.text), $("#"+page)[0]
and append each of them, instead of replacing, leaving only the last item in the list.
Where the #start element is the starting container, and I want to populate it with multiple elements, but I need to give each their own container, otherwise they will all overwrite eachother, the default reactjs behaviour.
I'm wondering if there's a way to tell react to append instead of replacing the div.
I'd like to avoid using jquery if possible, and do it purely react-ly.
I though about giving the React.renderComponent page the initial list, and then iterate in the previously called template, however, then i'm facing a different problem, I have to return a reactjs element object, consisting of the whole list, which I really don't prefer.
I need for the initial call to create individual, independent react templates, appending eachother in a list, prefferably without any extra containers, or using jquery.
I think you're getting the concept wrong. React's renderComponent indeed renders a single component, somewhere. Doing this multiple times only re-renders the same component at that place (aka idempotent). There's no real "append" statement, at least not in the way you asked for.
Here's an example of what you're trying to achieve. Forgot about renderComponent in this. It's just to put the component somewhere.
/** #jsx React.DOM */
var pages = [{title: 'a', text: 'hello'}, {title: 'b', text: 'world'}];
var App = React.createClass({
render: function() {
return (
<div>
{
this.props.pages.map(function(page) {
return <div>Title: {page.title}. Text: {page.text}</div>;
})
}
</div>
);
}
});
React.renderComponent(<App pages={pages} />, whateverDOMNodeYouWantItToBeOn);
See what I did there? If I want multiple divs, I just create as many as I want to see. They represent the final look of your app, so making a same div be "appended" multiple times doesn't really make sense here.
Create a div with a class extradiv:
<div class="extradiv">
</div>
In CSS, Set It's display to none:
.extradiv {
display: none;
}
.extradiv * {
display: none;
}
In JS implement this function:
function GoodRender(thing, place) {
let extradiv = document.getElementsByClassName('extradiv')[0];
ReactDOM.render(thing, extradiv);
extradiv = document.getElementsByClassName('extradiv')[0];
place.innerHTML += extradiv.innerHTML;
}
Than you can use this in place of ReactDOM.render:
GoodRender(<Component>My Text</Component>, YourDOMObject)

Categories

Resources