Javascript export a new object instance - javascript

export utilities = {
assignOptions: function(newOption, callback) {
//Incorrect 'this' context on second binding
this.props.updateOptions((prevOptions) => Object.assign({}, prevOptions, newOption), callback);
}
};
export const FirstComponent = (WrappedComponent) => class extends React.Component {
constructor(props) {
super(props);
utilities.assignOptions = utilities.assignOptions.bind(this);
utilities.assignOptions("test"); //'this' context is FirstComponent
}
};
export const SecondComponent = (WrappedComponent) => class extends React.Component {
constructor(props) {
super(props);
utilities.assignOptions = utilities.assignOptions.bind(this);
utilities.assignOptions("test2"); //'this' context is still FirstComponent instead of SecondComponent
}
};
It seems to be the same instance that's getting exported and so the bindings aren't working.
How do I export a new instance each time?

The issue is likely how JS handles context within objects vs ES6 classes. An easy way to do prop injection like what I think you're attempting is to create an HOC that will wrap your components:
function wrapComponent(ComponentToWrap) {
return class WrappedComponent extends React.Component {
render() {
return <ComponentToWrap {...this.props}/>
}
}
}
This is a simplistic example but think that you can use as much logic as you'd like to inject whatever props you'd like into each WrappedComponent.

Related

-React Native, what is the way of passing state data from A.js to B.js

I am looking for the way of passing state data from A.js to B.js
so I want to retrieve "DEF " as title[1] from A.js to B.js
A.js
export default class A extends Component {
constructor(props) {
super(props)
this.state = {
title: ["ABC","DEF","GHI"]
};
}
render(){return(<Text>{this.state.title[1]}</Text>)}
module.exports = A;
B.js
import {A} from "./A"
export default class B extends Component {
render(){return(<Text>{A.state.title[1]?? // not sure about this part
}</Text>)}
}
custom for you :
import {B} from './B'
export default class A extends Component {
constructor(props) {
super(props)
this.state = {
title: ["ABC","DEF","GHI"]
};
}
render(){return(<B titlePropForB={this.state.title})}
module.exports = A;
export default class B extends Component {
render(){
console.log(this.props.titlePropForB);
return(<Text>{this.props.titlePropForB[1]}</Text>)}
}
You need to use props to pass data between components.
Add the props in your parent you want to pass.
export default class A extends Component {
constructor(props) {
super(props)
this.props = {
title: ["ABC","DEF","GHI"]
};
}
render(){return(<Text>{this.props.title}</Text>)}
module.exports = A;
Access them in the child.
import {A} from "./A"
export default class B extends Component {}
render(){return(<Text>{this.props.data[1]}</Text>)}}
This is a good reference discussing the differences between props and state.
refer to my answer here Passing Props to Screens in React Native .
passing props within different components has an alternative way, that is by using Redux library for managing app state. it is simple and easy to understand.

Move function in React from component to referenced library

I'm learning React and I'm not sure how to setup this pattern. It could be something really easy I'm just missing.
I have a main component that controls state. It has all of the functions to update state and passes these down to child components via props. I've simplified the code to focus on one of these functions.
Here's the component now, all works as it should:
ManageMenu.js
import React from 'react'
class ManageMenu extends React.Component {
constructor() {
super()
this.toggleEditing = this.toggleEditing.bind(this)
// Set initial state
this.state = {
menuSections: []
}
}
toggleEditing(id) {
const menuSections = this.state.menuSections
menuSections.map(key => (key.id === id ? key.details.editing = id : ''))
this.setState({ menuSections })
}
render() {
return (
...
)
}
}
export default ManageMenu
The toggleEditing is passed via props to a child component that uses it to render an editing form if the edit button is clicked.
I have about 10 of these different functions in this component and what I would like to do is move them to an external lib/methods.js file and then reference them. Below is the code I would like to have, or something similar, but React doesn't like what I'm doing. Throws a syntax error:
Failed to compile.
Error in ./src/components/ManageMenu.js
Syntax error: Unexpected token
toggleEditing(id, menuSectionId, this.state, this)
Here is what I would like to do...
lib/methods.js
const toggleEditing = function(id, state, that) {
const menuSections = state.menuSections
menuSections.map(key => (key.id === id ? key.details.editing = id : ''))
that.setState({ menuSections })
}
module.exports = {
toggleEditing
}
And then in my component:
ManageMenu.js
import React from 'react'
import { toggleEditing } from '../lib/methods'
class ManageMenu extends React.Component {
constructor() {
super()
// Set initial state
this.state = {
menuSections: []
}
}
toggleEditing(id, this.state, this)
render() {
return (
...
)
}
}
export default ManageMenu
Any help is appreciated, thanks!
Thanks to #Nocebo, the answer on how to externalize functions is here:
Externalise common functions in various react components
In my particular situation,
I need to remove the “floating” toggleEditing(id, this.state, this) call in the middle of nowhere. Update: This error happens “because it is invoking a method within a class definition.” (see Pineda’s comment below)
Remove the leading this. on the right side of the this.toggleEditing statement in constructor()
Update the function in lib/methods.js to remove the state and that variables since its bound to this in the constructor()
See updated code below.
ManageMenu.js
import React from 'react'
import { toggleEditing } from '../lib/methods'
class ManageMenu extends React.Component {
constructor() {
super()
this.toggleEditing = toggleEditing.bind(this)
// Set initial state
this.state = {
menuSections: []
}
}
render() {
return (
...
)
}
}
export default ManageMenu
lib/methods.js
const toggleEditing = function(id) {
const menuSections = this.state.menuSections
menuSections.map(key => (key.id === id ? key.details.editing = id : ''))
this.setState({ menuSections })
}
module.exports = {
toggleEditing
}
You're error arises because you are invoking toggleEditing in your ManageMenu.js class definition rather than defining a function.
You can achive what you want by setting a local class member this.toggleEditing to the bound function returned by the .bind method and do so within the constructor:
import React from 'react'
import { toggleEditing } from '../lib/methods'
class ManageMenu extends React.Component {
constructor() {
super()
this.state = {
menuSections: []
}
// bind external function to local instance here here
this.toggleEditing = toggleEditing.bind(this);
}
// don't invoke it here, bind it in constructor
//toggleEditing(id, this.state, this)
render() {
return (
...
)
}
}
export default ManageMenu

React Higher Order Component Initial Props

I am creating a player library and want the React flow to go like this:
PlayerHOC -> PlaylistHOC -> FooterContainer.
The reason I want it to go in this direction is that PlayerHOC has methods on it that PlaylistHOC and FooterContainer need to access (i.e from props).
My code:
class FooterContainer extends React.Component {
render() {
return (
<div>
<div className="jp-type-footer" >
//...
</div>
</div>
);
}
}
class FooterPlayer extends React.Component {
constructor() {
super();
this.options = {
smoothPlayBar: false,
muted: true,
//...
};
}
render() {
return (
<Player {...this.options} />
);
}
};
export const PlaylistHOC = (WrappedComponent) => class extends React.Component {
constructor(props) {
super(props);
//Add a new stateClass for the extra loop option
this.stateClass = merge({
shuffled: "state-shuffled",
loopedPlaylist: "state-loop-playlist"
}, this.props.stateClass);
}
setPlaylist = () => {}
};
export const PlayerHOC = (WrappedComponent) => class extends React.Component {
constructor(props) {
super(props);
//get passed in props from FooterPlayer and PlaylistHoc
}
play = () => {}
pause = () => {}
};
const Player = PlayerHOC(PlaylistHOC(FooterContainer));
export default connect()(FooterPlayer);
I also pass in props from FooterPlayer to PlayerHOC which works fine. However, I also want to pass in default props from PlaylistHOC to PlayerHOC that will never be updated and I can't figure out how to this while also keeping this flow.
For example: const Player = PlaylistHOC(PlayerHOC(FooterContainer)); this would allow me to pass in initial props from PlaylistHOC and FooterPlayer to PlayerHOC but then I would not be able to access PlayerHOC methods by props.
How do I do this?
I would use const Player = PlaylistHOC(PlayerHOC(FooterContainer)); because a parent component can't receive props from it's children.
Looks like both PlaylistHOC and PlayerHOC are mixins, so they should inherit from the component being wrapped instead of React.Component.
I've changed a code just a little bit to be able to test it, but the key idea of it is how I've extended WrappedComponent instead of React.Component in your mixins.
class FooterContainer extends React.Component {
render() {
return (
<div>
<div className="jp-type-footer">
<button onClick={this.play.bind(this)}>Play</button>
</div>
</div>
);
}
}
class FooterPlayer extends React.Component {
constructor() {
super();
this.options = {
smoothPlayBar: false,
muted: true
//...
};
}
render() {
return (
<Player {...this.options} />
);
}
};
export const PlaylistHOC = (WrappedComponent) => class extends WrappedComponent {
constructor(props) {
super(props);
//Add a new stateClass for the extra loop option
//this.stateClass = merge({
// shuffled: "state-shuffled",
// loopedPlaylist: "state-loop-playlist"
//}, this.props.stateClass);
}
setPlaylist() {
}
};
export const PlayerHOC = (WrappedComponent) => class extends WrappedComponent {
constructor(props) {
super(props);
//get passed in props from FooterPlayer and PlaylistHoc
}
play() {
console.log('playing');
}
pause() {
}
};
const Player = PlaylistHOC(PlayerHOC(FooterContainer));
export default connect()(FooterPlayer);
By the way, try decorators for some really fancy syntax like
#PlayerlistHOC
#PlayerHOC
class FooterContainer {
}
Be warned decorators are not definitive and might change a lot.

How do you test an abstract class in Enzyme and/or pass implicit variables?

I have a base class that's being extended by several components, and inside that base class, there's an implicit variable that is being passed in from the classes that are extending it. For instance, I have the following as the base class:
export default class BaseCard extends React.Component {
constructor(props, context) {
super(props, context);
}
render() {
return (
<div>
{this.hasData && <span> Has Content </span>}
</div>);
}
}
And the component that extends the BaseCard:
export default class MyCard extends BaseCard {
constructor(props) {
super(props);
this.hasData = true;
}
render() {
return (
<div>
MyCard content
</div>);
}
}
this.hasData is defined inside the MyCard component, but since I'm testing BaseCard, it's not defined inside the class, and therefore, I can't test parts of the DOM that depend on that variable being there. How can I pass it in when testing with Enzyme?
You can set the variable on the instance like this:
const wrapper = mount(<MyCard />);
wrapper.instance().hasData = true;

React - props is empty when calling a callback function from child

I have a button on my main component, when its clicked its open an "Approval pannel", And when the OK is clicked I am calling a callback function to the main component and doing some logic.
I want to pass the callback function(My reasons), The problem is that when the callback function is called, the props and state are undefined.
Why is that happening? Please tell me if any info is missing.
I have added a partial code here:
class MainComponent extends React.Component {
constructor(props){
currentActionConfig = {onOkClick: this.onGenericApprovalOkClicked, ...};
}
onCommandApprovalOkClicked(commandText){
console.log(this.props); <- 'undefined'
}
render(){
return <ActionsApprovalPanel currentActionConfig={this.currentActionConfig}/>
}
}
export default class ActionsApprovalPanel extends React.Component {
render()
{
...
return <ChangeIpApproval onOkClick={this.props.currentActionConfig.onOkClick}/>;
...
}
}
Try these changes
class MainComponent extends React.Component {
constructor(props){
super(props); //1. Call super
this.currentActionConfig = {onOkClick: this.onGenericApprovalOkClicked.bind(this), ...}; // 2.bind this
}
onCommandApprovalOkClicked(commandText){
console.log(this.props); <- 'undefined'
}
render(){
return <ActionsApprovalPanel currentActionConfig={this.currentActionConfig}/>
}
}
export default class ActionsApprovalPanel extends React.Component {
render()
{
...
return <ChangeIpApproval onOkClick={this.props.currentActionConfig.onOkClick}/>;
...
}
}
I think you need to make few changes to your React component.
First: In the constructor call super().
Second:: Define currentActionConfig as a state and try using it as this.state.currentActionConfig
Third: Specify the binding on onCommandApprovalOkClicked(). as
onCommandApprovalOkClicked = (commandText) => {} and similary for other functions.
class MainComponent extends React.Component {
constructor(props){
super(props);
this.state = {
currentActionConfig = {onOkClick: this.onGenericApprovalOkClicked, ...}
};
}
onCommandApprovalOkClicked(commandText){
console.log(this.props); <- 'undefined'
}
render(){
return <ActionsApprovalPanel currentActionConfig={this.state.currentActionConfig}/>
}
}
export default class ActionsApprovalPanel extends React.Component {
render()
{
...
return <ChangeIpApproval onOkClick={this.props.currentActionConfig.onOkClick}/>;
...
}
}
Make these changes and see if they work.

Categories

Resources