Categories
discuss

Scope composables to a parent composable

I’m making an implementation of PreferencesScreen in Compose and I’ve made all the components like PreferencesSwitch, CheckBox, etc.
Now I’m wondering if there is any way to make it so all the components can only be used inside the scope of the PreferencesScreen function and cannot be used outside of it.
Like for example, in LazyColumn, items can only be used inside LazyColumnScope. I looked at the implementation of it but it used the annotation @LazyScopeMarker so I’m assuming there’s different markers for different scopes?

Expected Behaviour:

PreferencesScreen{
    PreferencesCheckBox(...){ ... }
}

is possible but,

PreferencesCheckBox(...){ ... }

alone is not possible.

Answer

You can declare some scope same like LazyColumn does:

interface PreferencesScreenScope {
    @Composable
    fun PreferencesCheckBox()
}

private class PreferencesScreenScopeImpl: PreferencesScreenScope {
    @Composable
    override fun PreferencesCheckBox() {

    }
}

interface/class ...Impl is used here to make sure that no other screen can reuse PreferencesScreenScopeImpl, also it adds testing possibility.

Use it in PreferencesScreen:

@Composable
fun PreferencesScreen(content: @Composable PreferencesScreenScope.() -> Unit ) {
    PreferencesScreenScopeImpl().content()
}

Use PreferencesScreen like this:

PreferencesScreen {
    PreferencesCheckBox()
}
Categories
discuss

React: make a cycle-through component with both forward and backward directions using Generator

I have an array of strings to display const array = ["one", "two", "three"]; .

The UI initially shows the first item in the array i.e. "one". From there I have a button right when clicked it shows the next item or string which is two, and then three, after three it should go back to one and start from there again. I also have a left button, when clicked it shows the previous item or string, if the current string is two, the previous string is one, and then after one it starts from three and walks backward.

I am using generator to do it. Here is my attempt

function* stepGen(steps) {
  let index = 0;
  while (true) {
    const direction = yield steps[index];
    index = (index + (direction === "forward" ? 1 : -1)) % steps.length;
  }
}

const array = ["one", "two", "three"];
let gen = stepGen(array);
const getNext = () => gen.next("forward").value;
const getPrev = () => gen.next("backward").value;

export default function App() {
  const [current, setCurrent] = useState(() => getNext());
  const onRight = () => {
    const next = getNext();
    setCurrent(next);
  };
  const onLeft = () => {
    const prev = getPrev();
    setCurrent(prev);
  };

  return (
    <div className="App">
      <h1>{current}</h1>
      <button onClick={onLeft}>left</button>
      <button onClick={onRight}>right</button>
    </div>
  );
}


Here is a live demo you can play with https://codesandbox.io/s/cyclethrough1-deh8p?file=/src/App.js

Apparently the current behavior is buggy. There are multiple issues that I don’t know the causes and the solutions:

  1. the UI starts with two not one. I guess it has something to do with how I initiate my state current
const [current, setCurrent] = useState(() => getNext());

I thought () => getNext() is only to get called once when the component first mounts so current should be one from the start.

And I tried to initiated the state with

const [current, setCurrent] = useState(array[0]);

It indeed starts with the first item in the array which is one but you have to click right button twice to make it go to two. Here is the live demo for this variation https://codesandbox.io/s/cyclethrough2-5gews?file=/src/App.js

  1. the left button, which should walk backward the loop doesn’t work. It is broken completely. the right button works though. Not sure why.

Answer

The problem with getPrev is the remainder (%) operator, which unlike the modulo operation returns a negative result when the remainder is negative. To solve that use a modulo function instead:

// modulo function
const mod = (n, r) => ((n % r) + r) % r;

To solve the problem on the 1st render create the initial value outside of the component. This is a workaround, since I can’t find the reason for that bug.

const init = getNext(); // get the initial value

export default function App() {
  const [current, setCurrent] = useState(init); // use init value

I would also save the need for a ternary to determine the increment by passing 1 and -1 in getNext and getPrev respectively.

Full code example (sandbox):

// modulo function
const mod = (n, r) => ((n % r) + r) % r;

function* stepGen(steps) {
  let index = 0;
  while (true) {
    const dir = yield steps[index];
    index = mod(index + dir, steps.length); // use mod function instead of remainder operator
  }
}

const array = ['one', 'two', 'three'];
const gen = stepGen(array);

const getPrev = () => gen.next(-1).value; // dec directly
const getNext = () => gen.next(1).value; // inc directly

const init = getNext(); // get the initial value

export default function App() {
  const [current, setCurrent] = useState(init); // use init value

  const onLeft = () => {
    const next = getPrev();
    setCurrent(next);
  };

  const onRight = () => {
    const prev = getNext();
    setCurrent(prev);
  };

  return (
    <div className="App">
      <h1>{current}</h1>
      <button onClick={onLeft}>left</button>
      <button onClick={onRight}>right</button>
    </div>
  );
}
Categories
discuss

Java heap memory allocation limits

I’ve written the following test to check maximum available heap memory:

import java.util.*;
public class Memory {
    public static void main(String[] args) {
        long maxMB = Runtime.getRuntime().maxMemory() / 1048576L;
        System.out.println("Maximum memory is " + maxMB + " MB.");
        ArrayList<byte[]> allocated = new ArrayList<>();
        try {
            while (true)
                allocated.add(new byte[1024*1024]);
        } catch (OutOfMemoryError e) {
            System.out.println("allocated " + allocated.size() + " MB before running out of memory.");
        }
    }
}

However, when I test this, it appears that only half of the “available” memory can actually be allocated:

$ java -Xmx512m Memory 
Maximum memory is 512 MB.
allocated 255 MB before running out of memory.
$ java -Xmx1024m Memory 
Maximum memory is 1024 MB.
allocated 511 MB before running out of memory.

Anyone know why this would be the case?

Answer

I believe what happens is that the memory manager tries to align the chunks at the next available 1MB boundary. But as the 1MB arrays actually take up slightly more than 1MB (for storing length and something else), they get arranged with a gap of almost 1MB between them. When reducing the block size by 16 bytes, they suddenly use up the whole memory again.

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..