How to access elements rendered in another React component? - reactjs

I have an app that looks roughly like this:
class App extends React.Component {
render(){
var ComponentTwo = this.props.getComponentTwo();
return (
<div>
<ComponentOne />
{ComponentTwo ? <ComponentTwo /> : []}
</div>
);
}
}
The ComponentTwo is not always the same component class and sometimes it needs to interact with ComponentOne - namely - select certain elements from it and attach some listeners to them.
I want this functionality to come with ComponentTwo because it's only needed when ComponentTwo is loaded and it comes with a lot of code.
What are the best practice to allow one component to dynamically extend another sibling component?

I believe that the correct way to implement such a scenario in React is to change state of the App component after some action in ComponentTwo and then pass state changes to ComponentOne:
onComponentTwoAction: function(someValueFromAction) {
this.setState({
componentOneProp: someValueFromAction
});
},
render: function(){
var ComponentTwo = this.props.getComponentTwo();
return (
<div>
<ComponentOne componentOneProp={this.state.componentOneProp}/>
{ComponentTwo ? <ComponentTwo onAction={this.onComponentTwoAction}/> : []}
</div>
);
}

Related

Persist dom state on route change

I have two components each on separate routes. I would like to know how I can keep the DOM elements in the same state on route change. For example I would like for all the DOM elements to have the same css classes applied as before the route change when navigating back to the same component.
I have tried redux persist and using nested routes with switch but none of these seem to work. From the research I have done it appears that React always mounts and unmount the component on route change and I haven't' been able to find a way to prevent this happening.
I would like for the red background color to remain when going back to test1.
class test1 extends React.Component {
constructor(props) {
super(props);
}
addClassFucn = event => {
$(event.target).parent().css("background-color", "red")
}
renderButton() {
return (
<div>
<div>
<button onClick={this.addClassFucn}>Click me</button>
<Link to="/test2" className="ui button primary back" >
test2
</Link>
</div>
</div>
)
}
render() {
return (
<div>
<div>This is test 1{this.renderButton()}</div>
</div>
);
}
}
export default test1;
class test2 extends React.Component {
constructor(props) {
super(props);
}
renderButton() {
return (
<div>
<Link to="/test1" className="ui button primary back" >
back
</Link>
</div>
)
}
render() {
return (
<div>
<div>This is test 2{this.renderButton()}</div>
</div>
);
}
}
export default test2;
It really depends on the logic of how you maintain the state of your component.
Redux persist should work. Just persist all the state that affects how the DOM currently displayed. Afterwards, inside the component, you should do a check whether there is a persisted state or not. If it is then you shouldn't do any change and just render.
You can use react's shouldComponentUpdate() which by default returns true allowing the component to re render. If useful than you can add the logic to return false which won't all the component to re-render. This is not recognized as best practice though you can refer this link for more details.

Reactjs: can I safely access a sibling component using state?

I'm using ReactJS since just a week or two and I'm now trying to build an App using it.
I think I understood how I should make a Child Component communicates with its Parent Component passing a function as a prop.
But now I'd like to do something different and make 2 sibling Components communicate with each other.
I know I could achieve this using their common Parent Component, but I'd really love to declare some methods on one of those sibling Components and reuse them all over the App.
So here is my idea and my question: can I safely set the state of a Parent Component putting there the "this" from Child Component and then use this variable on other Components?
I already wrote this code and it's working, but I don't understand if this is a good approach or a bad one.
Here some parts of my code to let you see what I'm doing.
Parent Component:
class App extends Component{
state = {}
render(){
return <Router>
<div id="page">
<Header app={this} />
<div id="main" class="row">
<Sidebar app={this} />
<Content app={this} />
</div>
<Footer app={this} />
</div>
</Router>
}
}
Sidebar:
class Sidebar extends Component{
state = {menu: []}
componentDidMount() {
this.props.app.setState({sidebar: this})
}
populateSidebar = (sidebar) => {
this.setState({menu: sidebar})
}
render(){
if (this.state.menu.length == 0){
return null;
}
return (
<sidebar class="col-3">
<ul>
{this.state.menu.map(item => <li><Link to={item.url}>{item.text}</Link></li>)}
</ul>
</sidebar>
)
}
}
User Component (it's a Child of the Content Component. The Content Component just does some routing based on the url):
class User extends React.Component {
async componentDidMount() {
await this.props.app.state.sidebar
this.props.app.state.sidebar.populateSidebar(
[
{
url: "/user/add",
text: "Add new user"
},
{
url: "/user/list",
text: "Users list"
}
]
)
}
async componentWillUnmount() {
await this.props.app.state.sidebar
this.props.app.state.sidebar.populateSidebar([])
}
render() {
return (
<div>
<UserAdd />
<UserList />
</div>
);
}
}
I know that what I'm accomplishing here is so basic that I could totally do it in a different way, for example putting the sidebar menu as an array on the Parent Component's state. But let's say that I want a bunch of methods on Sidebar and let all my other components using them without rewriting too much code. Is this a bad idea?
I'd really love to declare some methods on one of those sibling
Components and reuse them all over the App.
A better approach is to create a helper class with some static methods and use it everywhere across your components, this class even doesn't have to be a react component just a regular ES6 class, for example:
class MyHelper {
static doSummation(num1, num2) {
return num1 + num2;
}
static doMultiplication(num1, num2) {
return num1 * num2
}
// ... other helper methods as you want
}
export default MyHelper;
Then in your React components you can import it and use its helper methods:
import React, {Component} from 'react';
import MyHelper from './MyHelper';
class MyComponent extends Component {
render() {
return (
<div>
{MyHelper.doSummation(1, 2)};
</div>
);
}
}
You can even, for better organization, have as many helper classes as you want, for example MathHelper, StringFormattingHelper, etc...

Render a component when another component is clicked

I want to render BlackSpark when RedSpark is clicked, but I'm not sure how to change the state of a component in another component. I know how to set state in the component itself, but how do I affect another component when I click a different component?
class BlackSpark extends React.Component {
render() {
return (
<div className="black"></div>
);
}
}
class RedSpark extends React.Component {
render() {
return (
<div className="red"></div>
);
}
}
class App extends React.Component {
render() {
return (
<div>
<BlackSpark />
<RedSpark />
</div>
);
}
}
In React, there's a concept of component composition as you've already embraced -- it allows you to accomplish what you want by rendering children based on the parent's state, another key concept known as lifting state up. What this means, is if you have mutually dependent components, create a single parent which composes them, and have state in the parent control the presentation and logic of the children. With the parent App, you can keep your state inside App, and based on App's state, conditionally render whatever you want -- either BlackSpark or both. For example, using the logical && operator:
{condition && <Component />}
This will only render <Component> when condition is truthy, or else it will not render anything at all (except for when condition is 0). Applying it to this situation, try adding state to your App component to utilize conditional rendering.
There's another key concept you need to understand: component props. They are essentially inputs to a component, certain properties passed to the component to tell how it should behave -- like attributes on regular HTML elements such as input placeholders, URLs, and event handlers. For example:
<Component foo="bar" bar={3} />
This will pass the props foo and bar down to Component with the values "bar" and 3 respectively and are accessible through this.props. If you were to access this.props.foo inside the Component component it would give you "bar". If you pair this up with composition, you can accomplish what you want:
class Example extends React.Component {
constructor() {
super();
this.state = {
showHello: true
}
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.setState(prevState => ({
showHello: !prevState.showHello
}));
}
render() {
return (
<div>
{this.state.showHello && <Child2 />}
This is a test.
<Child1 onClick={this.handleChange} />
</div>
);
}
}
class Child1 extends React.Component {
render() {
return <div onClick={this.props.onClick}>Click me!</div>
}
}
class Child2 extends React.Component {
render() {
return <div>Hello!</div>
}
}
ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
The above example lifts state up by having a parent compose the children and maintain the state. It then uses props to pass down an onClick handler to Child1, so that whenever Child1 is clicked, the state of the parent changes. Once the state of the parent changes, it will use conditional rendering to render <Child2> if the condition is truthy. Further reading at the React documentation and on the logical && operator.
I know how to set state in the component itself, but how do I affect another component when I click a different component?
The recommended way to do it would be to create a parent component that has the state. You'd then use that state to determine when to render the other child component.
I want to render BlackSpark when RedSpark is clicked, but I'm not sure how to change the state of a component in another component. Also, what if I want to hide BlackSpark when GreenSpark is clicked and GreenSpark is inside BlackSpark?
In this case, here's how you'd do it.
const GreenSpark = ({ onClick }) => (
<button className="green" onClick={onClick}>X</button>
)
const BlackSpark = ({ onClick }) => (
<div className="black">
<GreenSpark onClick={onClick} />
</div>
)
const RedSpark = ({ onClick }) => (
<div className="red" onClick={onClick}></div>
)
class Spark extends React.Component {
constructor(props) {
super(props)
this.state = {
showBlack: false
}
this.boundShowBlack = this.showBlack.bind(this)
this.boundHideBlack = this.hideBlack.bind(this)
}
showBlack() {
this.setState({ showBlack: true })
}
hideBlack() {
this.setState({ showBlack: false })
}
render() {
return (
<div>
<RedSpark onClick={this.boundShowBlack} />
{this.state.showBlack && <BlackSpark onClick={this.boundHideBlack} />}
</div>
)
}
}

React invariant violation when passing a component as a prop to another component

I'm working on a project that uses react-typeahead and attempting to implement a custom component for the dropdown list. React-typeahead accepts a customListComponent prop.
However, I need to pass a prop to the component that is being passed into the Typeahead component. Initially, I tried setting a variable as a the custom component:
//MainSearch.js
import SearchOrderComponent from './SearchOrderComponent'
export default class MainSearch extends React.Component {
//Constructor here
render() {
let customList = <SearchOrderComponent ranking={this.state.ranking} />
return(
<div className="search-container">
<Typeahead customListComponent={customList} />
</div>
)
}
}
This caused an invariant violation, with react stating that a react component was expected. My current workaround is to make SearchOrderComponent a function that accepts the paren't state as an input and returns a react component, like so:
//SearchOrderComponent.js
const wrapper = function(ranking){
let SearchOrder = React.createClass({
render: function() {
var searchRanking = ranking.map(function(item){
return <li key={item.key}>{item.value.niceName}</li>
});
return(
<div className='main-dropdown'>
{searchRanking}
</div>
);
}
});
return SearchOrder;
}
module.exports = wrapper;
Now I can input this function directly into the typeahead component:
//MainSearch.js
<Typeahead customListComponent={SearchOrderComponent(this.state.ranking)} />
But this feels like a break from the component API. Is there a more direct/proper way to do this?
If I understand correctly what you're trying to achieve, the most direct way would be
render() {
return(
<div className="search-container">
<Typeahead>
<SearchOrderComponent ranking={this.state.ranking} />
</Typeahead>
</div>
)
}
You can access the SearchOrderComponent from within the Typeahead component via this.props.children.

reactjs - Is it possible to pass a property to a component as following?

wonder if it is possible to pass a component a property as following
ReactDOM.render(
<ContainerBox anotherComponent={<AnotherComponent />} />, document.body);
And then insider the ContainerBox I want to pass AnotherComponent a property in following way.
class ContainerBox extends React.Component {
clickHandler() {
//does something fun
}
render () {
return (
this.props.anotherComponent(this.clickHandler) //<----- is it possible to pass properties from here?
);
}
}
Is it possible to pass things from ContainerBox to AnotherComponent from that position?
ContainerBox has a clickHandler function which I want to pass to AnotherComponent. It is possible to do so if I move <AnotherComponent /> to inside of render() instead. But then I cannot reuse ContainerBox for other components without first copying the whole ContainerBox.
Does it make sense? Hope you can understand.
UPDATED code example
Yes, that is possible. However, it's more common to do it like this
ReactDOM.render(
<ContainerBox><AnotherComponent /></ContainerBox>, document.body);
And in ContainerBox
class ContainerBox extends React.Component {
render () {
return (
this.props.children
);
}
}
Read more about reacts this.props.children here: https://facebook.github.io/react/docs/multiple-components.html#children
Edit:
I just want to point out that in this example, we are not passing a component, but an element (the result of rendering the component).
It's also possible to pass components, like this:
<Foo buttonComponent={FancyButtonComponent} />
and in Foo:
render() {
Button = this.props.buttonComponent;
return (
<div>
...
<Button />
</div>
);
}

Resources