Preact: Update props on root component

Created on 27 May 2017  ·  4Comments  ·  Source: preactjs/preact

Hi! Thank you very much for Preact! Been experimenting with it quite a bit.

I’m trying to “implement my own simple Redux”: I have a store object with state that I pass to root component as a prop. And when I update store I want Preact to get that new version passed to it in props.store. With React I am able to do it like this:

https://www.webpackbin.com/bins/-KlAbTdBluO_6Y6jLd-G

const React = require('react')
const ReactDOM = require('react-dom')
const App = require('./app.js')

let store = {
  title: 'Default title',
  timestamp: 0,
  bla: 'Oh, bla'
}

function update (patch) {
  store = Object.assign({}, store, patch)
  ReactDOM.render(<App store={store} update={update} />, document.body)
}

//let el = ReactDOM.render(<App store={store} update={update} />, document.body)

setInterval(function () {
  update({timestamp: Date.now()})
}, 100)

setTimeout(function () {
  update({title: 'Wow, updated'})
}, 2000)

No luck so far with Preact, however — it keep the first version of store object passed to it:

https://www.webpackbin.com/bins/-KlA_mC8-Ao4z8hg7sGH

const { h, render, Component } = require('preact')
const App = require('./app.js')

let store = {
  title: 'Default title',
  timestamp: 0,
  bla: 'Oh, bla'
}

let el = render(<App store={store} update={update} />, document.body)

function update (patch) {
  store = Object.assign({}, store, patch)
  render(<App store={store} update={update} />, document.body, el)
}

setInterval(function () {
  update({timestamp: Date.now()})
}, 100)

setTimeout(function () {
  update({title: 'Wow, updated'})
}, 2000)

I read that this is the way to update props on root component in React.

Thank you!

question

Most helpful comment

.setState() will update props, but those are immutable. The only way to set props at the root is to use a top-level render call:

const { h, render, Component } = require('preact')
const App = require('./app.js')

let store = {
  title: 'Default title',
  timestamp: 0,
  bla: 'Oh, bla'
}

function update (patch) {
  store = Object.assign({}, store, patch)
  // notice we re-assign to `el` here:
  el = render(<App store={store} update={update} />, document.body, el)
}

update({});

setInterval(function () {
  update({timestamp: Date.now()})
}, 100)

setTimeout(function () {
  update({title: 'Wow, updated'})
}, 2000)

I noticed you updated your webpackbin, looks like you got it covered! 😊

All 4 comments

Hi there! You're creating a new store object in each call to update(), but only "receiving" the store once (setting this.store in the App constructor). There are two options to fix this - move your store access into the render function so it accesses the new copy each time update() is called, or use lifecycle events on the App component to update `this.store).

Option 1:

class App extends Component {
  render (props) {
    // props is the same as this.props by the way :)
    console.log(this.props.store)
    return (
      <div>
        <h1>{this.props.store.title}</h1>
        <h2>{this.props.store.timestamp}</h2>
      </div>
    )
  }
}

Option 2:

This option updates this.store when props.store changes.

class App extends Component {
  constructor (props) {
    super(props)
    this.store = this.props.store
  }
  componentWillReceiveProps(props) {
    this.store = props.store;
  }
  render () {
    console.log(this.store)
    return (
      <div>
        <h1>{this.store.title}</h1>
        <h2>{this.store.timestamp}</h2>
      </div>
    )
  }
}

Let me know if that helps! :)

Thank you! So much. Solved!

I wonder why props wont update, if instead of duplicate render(<App store={store} update={update} />, document.body, el) in update() I’ll do el._component.render(), or .setState(), for instance. So the only way to update props from outside on the root is to render/mount the whole thing again.

My guess is that’s because setState does what it says on the box and doesnt update props, and render probably also just renders, but for some reason I expect it to get new props too :)

.setState() will update props, but those are immutable. The only way to set props at the root is to use a top-level render call:

const { h, render, Component } = require('preact')
const App = require('./app.js')

let store = {
  title: 'Default title',
  timestamp: 0,
  bla: 'Oh, bla'
}

function update (patch) {
  store = Object.assign({}, store, patch)
  // notice we re-assign to `el` here:
  el = render(<App store={store} update={update} />, document.body, el)
}

update({});

setInterval(function () {
  update({timestamp: Date.now()})
}, 100)

setTimeout(function () {
  update({title: 'Wow, updated'})
}, 2000)

I noticed you updated your webpackbin, looks like you got it covered! 😊

Yes, it’s covered! Thank you for clarifications.

Was this page helpful?
0 / 5 - 0 ratings