static props evaluation resulting in exception - reactjs

I am calling the below SomeComponent like following. SomeComponent.render is happening fine. But When i call show method I get undefined is not an object 'arr[index].something'
<SomeComponent arr={arr} />
export default class SomeComponent extends Component {
static propTypes = {
arr: PropTypes.arrayOf(PropTypes.any).isRequired,
};
state = {
x: 1,
x: 2,
};
show(index) {
const {arr} = this.props;
if(arr[index].something){ // Here i get the issue. Seems likes arr is undefined.
}
}
render() {
const {arr} = this.props;
return(
<Viewer images={arr} show={currentIndex => this.show(currentIndex)} />
)
}
}

show = (index) =>{
const {arr} = this.props;
if(arr[index].something){ // Here i get the issue. Seems likes arr is undefined.
}
}
change this function to this way
this.show = this.show.bind(this)
or add this line to constructor,(this.props) instance of component can't accessible in function without binding function

Related

Pass original component's function to react HOC

I have created react HOC component as below.
const UpdatedComponent = (OriginalComponent) => {
class NewComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
counter:0
}
}
componentDidMount(){
}
incrementCount = () => {
this.setState(prevState => {
return {counter:prevState.counter+1}
})
}
render(){
return <OriginalComponent
incrementCount={this.incrementCount}
count={this.state.counter}
/>
}
}
return NewComponent
}
export default UpdatedComponent
and I am using that component in the below example
class HoverCounter extends Component {
componentDidMount(){
}
handleMessages = () => {
// need to do somthing
}
render() {
const {incrementCount, count} = this.props
return (
<div onMouseOver={incrementCount}>
Hoverd {count} times
</div>
)
}
}
export default UpdatedComponent(HoverCounter)
I want to know that is it possible to pass
handleMessages()
function to HOC?
like this
export default UpdatedComponent(HoverCounter,handleMessages)
I have no idea how to pass the original component function or props to HOC.
you could get everyThing in your Hoc like this :
const UpdatedComponent = (OriginalComponent , func) => {
componentDidMount(){
func()
}
in HoverCounter also you could add this changes:
static handleMessages(){
// need to do something
}
export default UpdatedComponent(HoverCounter , HoverCounter.handleMessages)

Call a function of one of a list of children components of the parent component using reference

For my website I want to include a feature that helps users randomly click a link programatically. The event happens in the parent component called StreamingPlaza, and its has a list of children components called StreamingCard, each containing a streaming link. Below is my code:
StreamingPlaza
class StreamingPlaza extends Component {
state = {
......
}
roomclicks = [];
componentDidMount() {
//Approach 1//
this.roomclicks[0].current.handleClick();
//Approach 2//
this.roomclicks[0].props.click = true;
......
}
setRef = (ref) => {
this.roomclicks.push(ref);
}
renderRoom = (room) => {
return <StreamingCard info={room} ref={this.setRef} click={false}></StreamingCard>;
}
render () {
const rooms = this.props.rooms;
return (
{ rooms && rooms.map (room => {
return this.renderRoom(room);
})
}
);
}
StreamingCard
class StreamingCard extends Component {
constructor(props){
super(props);
this.state = {
......
}
}
handleClick = () => {
document.getElementById("link").click();
}
render() {
return (
✔️ Streaming Link: <a id="link" href=......></a>
);
}
Regarding Approach 1, the console reported the error Cannot read property handClick of undefined. After I removed "current", it said that this.roomclicks[0].handleClick is not a function. Regarding Approach 2, I was not able to modify the props in this way, as the console reported that "click" is read-only.
Approach 1 is basically how its need to be done, but with React API.
See React.createRef
class StreamingPlaza extends Component {
roomclicks = React.createRef([]);
componentDidMount() {
// 0 is room.id
this.roomclicks.current[0].handleClick();
}
renderRoom = (room) => {
return (
<StreamingCard
info={room}
ref={(ref) => (this.roomclicks.current[room.id] = ref)}
click={false}
></StreamingCard>
);
};
render() {
const rooms = this.props.rooms;
return rooms.map((room) => {
return this.renderRoom(room);
});
}
}

Mobx store update when React props change

I'm creating a generic react component, I'm using mobx internally to control the component state. What I need to achieve is besides to keep all business logic in Store, when the user change the showSomething prop, the store should know it so fetchSomeStuffs runs and change anotherThing.
// Application using the component
#observer
class MyApplication extends React.Component {
#observable
showSomething = false;
changeThings = () => {
this.showSomething = true;
};
render() {
return (
<React.Fragment>
<button onClick={this.changeThings}>Change Show Something</button>
<MyComponent showSomething={showSomething} />
</React.Fragment>
);
}
}
class Store {
#observable
showSomething = false;
#observable
anotherThing = [];
#action
setShowSomething = value => {
this.showSomething = value;
};
// I'll dispose this later...
fetchSomeStuffs = autorun(() => {
const { showSomething } = this;
// Update some other stuffs
if (showSomething) {
this.anotherThing = [1, 2, 3];
} else {
this.anotherThing = [];
}
});
}
#observer
class MyComponent extends React.Component {
static propTypes = {
showSomething: PropTypes.bool
};
constructor() {
super();
this.store = new Store();
}
componentDidMount() {
const { setShowSomething } = this.store;
this.setSomethingDispose = autorun(() =>
setShowSomething(this.props.showSomething)
);
}
componentWillUnmount() {
this.setSomethingDispose();
}
render() {
return (
<Provider store={this.store}>
<MySubComponent />
</Provider>
);
}
}
#inject("store")
#observer
class MySubComponent extends React.Component {
render() {
const { showSomething, anotherThing } = this.props.store;
return (
<div>
MySubComponent
{showSomething && "Something is finally showing"}
{anotherThing.map((r, i) => {
return <div key={i}>{r}</div>;
})}
</div>
);
}
}
This is the way i found to achieve it, all the logic is in the Store and I use an autorun in the componentDidMount of my main component to always keep the showSomething variable of the store the same as the prop.
My doubt here is if this is a good practice or if there are better ways to do it?
Yes, there is a better way, using computed values.
One pattern is to use a private variable to hold the values and a computed value to expose what you want.
using this pattern you can implement list filters and all sorts of dynamic computations with a lot of agility, since MobX optimizes all computed values.
Just remember that to access a computed value you just have to read it as a property, not a function.
Ex:
// good
store.anotherThing
// bad
store.anotherThing()
class Store {
#observable
showSomething = false;
#observable
_anotherThing = [];
#action
setShowSomething = value => {
this.showSomething = value;
};
#computed get anotherThing() {
const { showSomething } = this;
// Update some other stuffs
if (showSomething) {
this._anotherThing = [1, 2, 3];
} else {
this._anotherThing = [];
}
}
}

How do I set default props for a component wrapped in a Raley container?

I have a component wrapped in a Relay container:
const Widget = (props) => {
return (
<div> { props.foo.bar } </div>
)
}
Widget.defaultProps = { foo: {bar: 0} }
export default Relay.createContainer(Widget, {
fragments: {
store: () => Relay.QL`
fragment on WidgetData {
foo {
bar
}
}
`
}
})
I'm using a GraphQL-as-a-service provider for a backend, and when the foo property of WidgetData isn't present, it returns null, which throws when my component attempts to render props.null.bar
As far as I know, defaultProps only works when the prop is undefined, not when it's null.
How can I protect against null results with defaultProps in this case?
Hoping to avoid writing a guard statement for each prop I reference, if possible.
You can do a sanity check for props from Relay container.
render() {
let {
foo,
...other
} = this.props;
let bar = foo ? foo.bar : '';
return (
<div> { bar } </div>
)
}
For more info, checkout: https://github.com/bfwg/relay-gallery

function argument in component's constructor in react.js

When I look at the following line in this example:
const SortableItem = SortableElement(({value}) => <li>{value}</li>);
then I don't understand where is the lambda function ({value}) => <li>{value}</li> used in SortableElement ?
Can someone please enlighten me ?
SortableElement's code:
import React, {Component, PropTypes} from 'react';
import {findDOMNode} from 'react-dom';
import invariant from 'invariant';
// Export Higher Order Sortable Element Component
export default function SortableElement (WrappedComponent, config = {withRef: false}) {
return class extends Component {
static displayName = (WrappedComponent.displayName) ? `SortableElement(${WrappedComponent.displayName})` : 'SortableElement';
static WrappedComponent = WrappedComponent;
static contextTypes = {
manager: PropTypes.object.isRequired
};
static propTypes = {
index: PropTypes.number.isRequired,
collection: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
disabled: PropTypes.bool
};
static defaultProps = {
collection: 0
};
componentDidMount() {
let {collection, disabled, index} = this.props;
if (!disabled) {
this.setDraggable(collection, index);
}
}
componentWillReceiveProps(nextProps) {
const {index} = this.props;
if (index !== nextProps.index && this.node) {
this.node.sortableInfo.index = nextProps.index;
}
if (this.props.disabled !== nextProps.disabled)
{
let {collection, disabled, index} = nextProps;
if (disabled) {
this.removeDraggable(collection);
}
else {
this.setDraggable(collection, index);
}
}
}
componentWillUnmount() {
let {collection, disabled} = this.props;
if (!disabled) this.removeDraggable(collection);
}
setDraggable(collection, index){
let node = this.node = findDOMNode(this);
node.sortableInfo = {index, collection};
this.ref = {node};
this.context.manager.add(collection, this.ref);
}
removeDraggable(collection) {
this.context.manager.remove(collection, this.ref);
}
getWrappedInstance() {
invariant(config.withRef, 'To access the wrapped instance, you need to pass in {withRef: true} as the second argument of the SortableElement() call');
return this.refs.wrappedInstance;
}
render() {
const ref = (config.withRef) ? 'wrappedInstance' : null;
return (
<WrappedComponent ref={ref} {...this.props} />
);
}
}
}
export default function SortableElement (WrappedComponent, config = {withRef: false}) {
return class extends Component {
...
render() {
const ref = (config.withRef) ? 'wrappedInstance' : null;
return (
<WrappedComponent ref={ref} {...this.props} />
);
}
}
}
Look at the render() method in the returned React Component by the higher order SortableElement function, the lambda function (which is a stateless component) is passed as the first argument to the higher order function, and this first argument is going to end up as being the parameter WrappedComponent you see in the signature of this higher order function.
So this higher order function is going to spit a React component with a render() method that uses/calls your actual React component that you just passed in (the lambda function).
<WrappedComponent ref={ref} {...this.props} />
Thanks to ({value}) => <li>{value}</li> in
const SortableItem = SortableElement(({value}) => <li>{value}</li>);
we will be actually rendering an li element with a value passed as a prop from the map method below.
const SortableList = SortableContainer(({items}) => {
return (
<ul>
{items.map((value, index) =>
<SortableItem key={`item-${index}`} index={index} value={value} />
)}
</ul>
);
});
In the context of the SortableElement's API code the important thing is that it renders the WrappedComponent (lines 67-69). We can treat SortableElement as any other Higher Order Component - a component that wraps another component to deliver some extra functionality. In this case - a fancy sorting animation of the lambda function.
Put simply
({value}) => <li>{value}</li> is a short hand for
React.crateClass({
render:function(){
return <li>{this.props.value}</li>
}
})
refer to pure functional component in React
and SortableElement is a higher order component wrapping another React component ,such the functional component above

Resources