9

I am trying to integrate react into my angularjs webapp.

In my controller I create the component, which initially has array props that are empty. When the app is done initializing, I want to update the props again. Do I do this by calling ReactDOM.render() again or can I keep a reference to this instance and just do something like updateProps(newProps)?

This is called from my controller:

ReactDOM.render(
    <NavBar
        currencyTabs = {[]}
    />, document.getElementById("navbar-root")
);

And then when the data is done loading i need to update the currencyTabs with a full array ...

I understand how react components props update from parent to child, but I don't quite get how I can do this from plain JS.

rex
  • 3,133
  • 6
  • 35
  • 62
  • what is the idea of using react js in angularjs application – Sangram Badi Feb 07 '18 at 10:53
  • @SangramBadi Say you have a legacy AngularJS app in, idk, 2018 or 2022. It's easy to move "[greenfield](https://en.wikipedia.org/wiki/Greenfield_project)" work to a new, modern templating library, like React, but what about adding new components in the legacy system? If you continue to work in AngularJS, you're piling on tech debt! Instead, you want to embed a new template library (say [Preact](https://preactjs.com)) inside of the legacy one and eat legacy code away from the inside. /scene No, you do not use AngularJS as a wrapper for React in a greenfield project, if that confused you. – ruffin Mar 30 '22 at 14:05

2 Answers2

7

There is no magic at work here, you just need to re-render it.

Just wrapper your rendering into a function, eg:

function renderReactNavbar( tabs = [] ) {
  ReactDOM.render(
    <NavBar
        currencyTabs = { tabs }
    />, document.getElementById("navbar-root")
  );

}

and call it after you load / update your data.

Alternatively, you choose to load your data from inside react, which might be a better choice in the long run.

If you have internal state, this might be somewhat harder to handle. You could consider moving to a props only component (but since you don't share any relevant code of your react component, it is hard to say)

A small example of how it could look would be

// the render method, takes a component and props, and renders it to the page
function renderComponent( component, props ) {
  const target = document.querySelector('#container');
  ReactDOM.render( React.createElement( component, props ), target );
}

// gets the tabs from the input field, splits based on ,
function getTabsFromInput() {
  return document.querySelector('#tabs').value.split(',');
}

// small presentational component, shows the tabs and redirects selection changes through a callback
const Tabs = ({ tabs, selectedTab, onSelectionChanged }) => {
  return tabs && <div>{ tabs.map( (tab, key) => {
    return <h1 key={key} className={classNames( { 'active': key === selectedTab } ) } onClick={ () => onSelectionChanged( key ) }>{ tab }</h1>;
  } ) }</div>;
};

// some initiations
window.addEventListener('load', function() {
  // keep a local variable with the data
  let defaultProps = {
    onSelectionChanged: updateSelection,
    selectedTab: 0,
    tabs: getTabsFromInput()
  };

  // handles selection changes
  function updateSelection( newTab ) {
    defaultProps = {...defaultProps, selectedTab: newTab };
    renderComponent( Tabs, defaultProps );
  }

  // adds an event listener for click events to initiate tab changes
  document.querySelector('#updateTabs').addEventListener('click', function() {
    defaultProps = {...defaultProps, tabs: getTabsFromInput() };
    // render on update
    renderComponent( Tabs, defaultProps );
  });

  // initial render
  renderComponent( Tabs, defaultProps );
});
.active {
  background-color: blue;
}
<script id="react" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.js"></script>
<script id="react-dom" src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.js"></script>
<script id="classnames" src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.2.5/index.js"></script><div id="container"></div>
<input type="text" value="tab1,tab2" id="tabs" />
<button id="updateTabs" type="button">Update tabs</button>
Icepickle
  • 12,689
  • 3
  • 34
  • 48
  • Thank you -this is what I was wondering. Unfortunately loading the data inside the component is not an option at this time due to the nature and complexity of our app. – rex Feb 07 '18 at 10:55
  • @rex Yeah, well then you have to make sure you reload at the correct times. So if you have a change in your data, just call the re-render. I am however unsure how internal state works in this case – Icepickle Feb 07 '18 at 15:44
  • 7
    This answer is great, but I misunderstood it. So, note that might help someone else: `ReactDom.render()` does not give you a clean-slate; it just updates the component. So if your props did not change, it will not re-render. If you are fighting a system that isn't totally functional, and you have to do `ReactDom.unmountComponetnAtNode()` first (or introduce some other prop just for the sake of forcing an update). Have it working? OK good, now look for some way to remove that hack :-) – John Hatton Oct 22 '19 at 17:08
  • 1
    @JohnHatton's comment is important, afaict, but unfortunately contains a typo in `unmountComponentAtNode`! Here's [`unmountComponentAtNode`](https://reactjs.org/docs/react-dom.html#unmountcomponentatnode) in React's docs, and [here's an answer from Dan Abramov](https://stackoverflow.com/a/48198011/1028230) saying why it's essential to use for [a subset of?] cases we're discussing, if I'm reading him right. – ruffin Mar 30 '22 at 14:21
0

Your NavBar component has a state that can be updated using the lifecycle methods. Without you code it is difficult to know for sure, but I think you want something like

class NavBar extends Component {
  state: { currencyTabs: [] }

  componentWillRecieveProps(nextProps) {
    if (this.props.currencyTabs !== nextProps.currencyTabs) {
      this.setState({ currencyTabs: nextProps.currencyTabs })
    }
  }

  render() {
    return (
      <div>
        {this.state.currencyTabs.map(button => (
          <span>{currency}</span>
        )}
      </div>
    )
  }
}

When the currencyTabs prop is changed in your ReactDOM.render method, the component will re-render and the changes will be reflected

  • 1
    It doesn't have state, it has props, at least from what we can see. If it has an internal state to manage the currencyTabs, that's another question. The lifecycle methods won't intervene here either, as this is directly rendered to a div element, react is simply used to render 1 single component) – Icepickle Feb 07 '18 at 15:34
  • I want to change just component and check the "componentWillRecieveProps". Then I apply some code in "componentWillRecieveProps" that is good working – spritecodej Dec 18 '19 at 14:04