yysun / apprun

AppRun is a JavaScript library for developing high-performance and reliable web applications using the elm inspired architecture, events and components.

Home Page:https://apprun.js.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Example of child component updating parent's state?

Geordi7 opened this issue · comments

I have a component that has its own state/view/update. The component validates user input according to rules it knows. Once a valid input is received it should notify its parent of the state change.

Here is a simplified example:

class C extends Component {
  state = {v: '', i: '', rules: []};
  view = s => <input $bind="i" />
  mounted = (p,c,s) => {
    s.v = p.choice
    s.i = p.choice
    s.rules = p.rules
  }
  update = {
    ???
  }
}

class App extends Component {
  state = {choice: '', rules: [...]}
  view = s => <div><C choice={s.choice} rules={s.rules} /></div>;
  update = {
    new_choice: (s,n) => {choice: n}
  }
}

How can C notify App of the new choice?

It looks like there are questions here.

  1. How to validate input? You can use $oninput (or $onchange) instead of $bind
  2. How to notify the parent? You can fire a global event
class C extends Component {
  state = {v: '', rules: []};
  view = s => <input $oninput="input" />
  mounted = (p,c,s) => {
    s.v = p.choice
    s.i = p.choice
    s.rules = p.rules
  }
  update = {
    input: (s, e) => {
      if(e.target.value === '1') app.run('@new_choice', 1);
    }
  }
}

class App extends Component {
  state = {choice: '', rules: []}
  view = s => <div>
    <div>{s.choice}</div>
    <C choice={s.choice} rules={s.rules} />
  </div>;
  update = {
    '@new_choice': (s,n) => ({choice: n})
  }
}
app.render(document.body, <App/>);

Or, you can pass the parent into the child if you want to keep the event local.

class C extends Component {
  state = {v: '', rules: []};
  view = s => <input $oninput="input" />
  mounted = (p,c,s) => p
  update = {
    input: (s, e) => {
      if(e.target.value === '1') s.parent.run('new_choice', 1);
    }
  }
}

class App extends Component {
  state = {choice: '', rules: []}
  view = s => <div>
    <div>{s.choice}</div>
    <C choice={s.choice} rules={s.rules} parent={this}/>
  </div>;
  update = {
    new_choice: (s,n) => ({choice: n})
  }
}
app.render(document.body, <App/>);

Also BTW, you can return a new state from the mounted function.

The above code has been tested in the Playground. https://apprun.js.org/#play

Thank you for this information, it is useful.

The problem I have with stateful components is when they send a message to the parent, all siblings are reset:

class Counter extends Component {
  state = {v: 0, i: 0};
	mounted = p => p;
	view = state => <>
        <h1>{state.i}: {state.v}</h1>
        <button $onclick={s => (s.v+=1, s)}>-1</button>
        <button $onclick={s => (s.v+=1, s)}>+1</button>
      	<button $onclick='ok'>ok</button>
      </>;
    update = {
      ok: s => (app.run('@set', s.i, s.v),s),
    }
}

class App extends Component {
  state = [0,0,0];
	view = s => <div>
        {s.join(',')}
      	<hr />
      {s.map((n,i) => <Counter v={n} i={i} />)}
      </div>
    update = {
      '@set': (s,i,v) => (s[i] = v, s),
	}
}

new App().start(document.body)

Try this in the playground: click all the + buttons, and then click one of the ok buttons to update the parent.