I have a Higher Order Component that receives another component as a parameter:
HOC
export default function HOC(Comp) {
return class extends Component {
doSomething() {
const temp = // the Comp's clientId prop???
}
........
}
}
Sub Component
#HOC
export default class SubComponent extends Component {
.....
static proptypes = {
clientId: PropTypes.string.isRequired
};
.......
}
Question:
Is it possible in the scenario above for the HOC to be aware of SubComponent's clientId property in its arguements and if so, how can I make the HOC aware of it for my doSomething function?
Since it's really the HOC that receives the props (or rather the component the function returns), you can just access them with this.props:
const temp = this.props.clientId;
Related
I have a Bluetooth class and listener method. I want to update my state in Bluetooth class and i will show in functional component.
JavaScript Class
import React, { Component } from "react";
import { MySampleContext } from "../../contexts/MySampleContext";
export class BluetoothClass extends Component {
static contextType = MySampleContext;
sampleBluetoothListener(value){
this.context.updateMyState(value);
}
}
This is my error.
TypeError: undefined is not an object (evaluating 'this.context.updateMyState')
Yes you can, you can pass data down to your component tree using Context API.
// Context file
import React from 'react';
const FormContext = React.createContext();
export default FormContext;
// Parent Class Component
import FormContext from "../context";
class ParentClass extends Component {
state = { name: "John Doe" };
render() {
return (
<FormContext.Provider value={this.state.name}>
<ChildClass />
</FormContext.Provider>
);
}
}
// Child Class Component
import FormContext from "../context";
class ChildClass extends Component {
render() {
return (
<FormContext.Consumer>
{(context) => {
console.log(context);
}}
</FormContext.Consumer>
);
}
}
The value prop in the context API takes an object, in which you could add a method that changes your state and pass it down to your component tree.
I advice you to take a quick look at the official Context API docs by React for a better understanding.
Thanks for all answers. But i mean pure "javascript class" not component, without rendering and react hooks is component based. Finally i solve problem with call back functions.
I'm learning about ErrorBoundaries in React and was wondering why and how in the getDerivedStateFromError method, is state being set this way?
// REACT
import React from 'react';
export class ErrorBoundary extends React.Component {
state = {
isThereError: false
}
static getDerivedStateFromError(error) {
return {
isThereError: true
};
}
// ...
};
Shouldn't it be:
// REACT
import React from 'react';
export class ErrorBoundary extends React.Component {
state = {
isThereError: false
}
static getDerivedStateFromError(error) {
this.setState({ isThereError: true })
}
// ...
};
State isn't being set in the method itself. But by convention the framework expects that method to return an updated state object. The framework then internally uses that result to update state:
This lifecycle is invoked after an error has been thrown by a descendant component. It receives the error that was thrown as a parameter and should return a value to update state.
So when implementing getDerivedStateFromError in your component you don't need to worry about updating state within that method. Just return the new state.
I have a Table component that I want ref to be attached to.
Use: Table.js
class Table extends React.Component {
constructor(props) {
super(props);
this.state = {
rows: 1,
dataLength: props.dataLength,
}
this.tableRef = React.createRef();
}
componentDidUpdate() {
//using ref
this.tableRef.current ..... //logic using ref
this.state.rows ..... //some logic
}
render() {
<TableContainer ref={this.tableRef} />
<CustomPagination />
}
}
This works fine, but now my requirement has changed, and I want to reuse the Table component with pagination applied to all the Tables in my App. I have decided to make a HOC withCustomPagination.
Use: withCustomPagination.js HOC
import CustomPagination from 'path/to/file';
const withCustomPagination = tableRef => Component => {
return class WithCustomPagination extends React.Component {
constructor(props) {
super(props);
this.state = {
rows: 1,
dataLength: props.dataLength,
}
}
componentDidUpdate() {
tableRef.current.state ..... //logic using ref, Error for this line
this.state.rows ..... //some logic
}
render() {
return (
<Component {...state} />
<CustomPagination />
)
}
}
}
export default withCustomPagination;
New Table.js:
import withCustomPagination from '/path/to/file';
const ref = React.createRef();
const Table = props => (
<TableContainer ref={ref} />
);
const WrappedTable = withCustomPagination(ref)(Table);
HOC withCustomPagination returns a class WithCustomPagination that has a componentDidUpdate lifecycle method that uses Table ref in the logic. So I try to pass ref created in Table.js as argument to withCustomPagination, i.e curried with ref and Table stateless component.
This use of ref is wrong and I get error: TypeError: Cannot read property 'state' of null.
I tried using Forwarding Refs, but was unable to implement it.
How do I pass the Table ref to withCustomPagination and be able to use it in HOC?
In this case you can use useImperativeHandle
It means you have to forward ref and specify which function or object or,...
you want to share with ref inside your functional component.
Here is my Hoc example :
import React from 'react';
import { View } from 'react-native';
export function CommonHoc(WrappedComponent) {
const component = class extends React.Component {
componentDidMount() {
this.refs.myComponent.showAlert();
}
render() {
return (
<>
<WrappedComponent
ref='myComponent'
{...this.state}
{...this.props}
/>
</>
);
}
};
return component;
}
and it's my stateless component
const HomeController=(props,ref)=> {
useImperativeHandle(ref, () => ({
showAlert() {
alert("called");
},
}));
return (
<Text>home</Text>
);
};
export default CommonHoc(forwardRef(HomeController));
Either restructure your code to not use a HOC for this or try using React.forwardRef:
Refs Aren’t Passed Through
While the convention for higher-order components is to pass through
all props to the wrapped component, this does not work for refs.
That’s because ref is not really a prop — like key, it’s handled
specially by React. If you add a ref to an element whose component is
the result of a HOC, the ref refers to an instance of the outermost
container component, not the wrapped component.
The solution for this problem is to use the React.forwardRef API
(introduced with React 16.3). Learn more about it in the forwarding
refs section.
via Higher-Order Components: Refs Aren’t Passed Through
In the forwarding refs section there are code examples you could use to pass refs down, but trying to yank them up will fail in your case with:
Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail.
In a project we took a different approach. There's an EnhancedTable component that handles all of the pagination logic and in itself has the dumb table component and the pagination component. It works pretty well but this means you would have to drill props (or use a store lib like Redux or Mobx) and add new ones that will handle pagination options. This will result in some refactoring of Table uses and you'll have to be more explicit but I would take it as a boon rather than a hindrance.
I was able to solve a simmilar issue that brought me to this thread without using forwardRef or useImperativeHandle.
By creating the ref at a higher level, and passign it down into the component and sub components that I needed to act on with the ref.
/** Parent Component has access to ref and functions that act on ref **/
import { useRef } from 'react';
const formRef = useRef(); // ref will have dom elements need accessing
const onClickFunction=()=>{ //sample function acts on ref
var inputs = formRef.current.querySelectorAll('input')
/* Act on ref here via onClick function, etc has access to dom elements
in child component and childs child components */
};
return(
<ComponentGetsAttachedRef formRef={formRef} />
//^ref sent down to component and its children
<ComponentNeedingRef onClickFunction={onClickFunction}/>
//^function with access to ref sent down to component
)
/** Child component needs to act on ref**/
export const ComponentNeedingRef = ({ onClickFunction}) =>{
return(
<button onClick={onClickFunction}>
)
}
/* Child component recieves ref and passes it down */
export const ComponentGetsAttachedRef = ({ formRef}) =>{
//ref comes in as prop gets attached to props or utilized internally
return (
<ChildsChildComponent formRef={formRef}/> //sub component passed ref down
)
}
I have a class "MessageDisplay" of which I want to call the function sendMassageToServer from the outside. I´ve built a helper function to call from another file. But how do you export functions that are inside classes?
These data are just examples.
main.js
export function sendSpeechToServer(query){
MessageDisplay.sendMessageToServer(query);
}
class MessageDisplay extends React.Component {
constructor(props) {
super(props);
this.state = {message : []};
}
(export const??) sendMessageToServer(searchQuery) {
...code
}
}
We are accesing the sendSpeechToServer() function from another file. Unortunately I am not even able to reach sendMessageToServer() from inside sendSpeechToServer().
Any help surely is appreciated. :)
EDIT:
The answer is found. For any other people:
export function sendSpeechToServer(query){
let test = new MessageDisplay();
test.sendMessageToServer(query);
}
Better way to separate component(MessageDisplay) and sendMessageToServer.
Then you can import sendMessageToServer awry where. You can inject sendMessageToServer like a props:
// main.js
import { sendMessageToServer } from './api';
<MessageDisplay sendMessage={sendMessageToServer} />
// MessageDisplay.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class MessageDisplay extends Component {
static propTypes = {
sendMessage: PropTypes.func.isRequired,
}
handleClick = (e) => {
e.preventDefault();
this.props.sendMessage();
};
render() {
return (<button onClick={this.handleClick}>Send to</button>)
}
}
export default MessageDisplay;
It useful for testing.
Instantiating a component manually for general purposes like let test = new MessageDisplay() is an antipattern, this indicates that a class is misused.
React component classes are primarily intended to make lifecycle hooks available and maintain state. They can sparsely benefit from inheritance (besides the relationship with React.Component) and other OOP traits.
The fact that it's possible to use component method as new MessageDisplay().sendMessageToServer(query) means that it was a mistake to make it component method in the first place. Classes aren't supposed to be glorified namespaces; ES modules play the role of namespaces in modern JavaScript.
A proper way is to extract the method and use it in both places as regular helper function. Functional approach is considered idiomatic in React.
export function sendSpeechToServer(query){
let test = new MessageDisplay();
test.sendMessageToServer(query);
}
it is bad, because you should not, create new react.component instance with new keyword,
better use static function like this
static sendMessageToServer(searchQuery) {
...code
}
and then
export function sendSpeechToServer(query){
MessageDisplay.sendMessageToServer(query);
}
I m actually learning reactjs and I m actually developping a little TODO list, wrapped inside of a "parent component" called TODO.
Inside of this parent, I want to get the current state of the TODO from the concerned store, and then pass this state to child component as property.
The problem is that I dont know where to initialize my parent state values.
In fact, I m using ES6 syntax, and so, I dont have getInitialState() function. It's written in the documentation that I should use component constructor to initialize these state values.
The fact is that if I want to initialize the state inside of my constructor, the this.context (Fluxible Context) is undefined actually.
I decided to move the initialization inside of componentDidMount, but it seems to be an anti pattern, and I need another solution. Can you help me ?
Here's my actual code :
import React from 'react';
import TodoTable from './TodoTable';
import ListStore from '../stores/ListStore';
class Todo extends React.Component {
constructor(props){
super(props);
this.state = {listItem:[]};
this._onStoreChange = this._onStoreChange.bind(this);
}
static contextTypes = {
executeAction: React.PropTypes.func.isRequired,
getStore: React.PropTypes.func.isRequired
};
componentDidMount() {
this.setState(this.getStoreState()); // this is what I need to move inside of the constructor
this.context.getStore(ListStore).addChangeListener(this._onStoreChange);
}
componentWillUnmount() {
this.context.getStore(ListStore).removeChangeListener(this._onStoreChange);
}
_onStoreChange () {
this.setState(this.getStoreState());
}
getStoreState() {
return {
listItem: this.context.getStore(ListStore).getItems() // gives undefined
}
}
add(e){
this.context.executeAction(function (actionContext, payload, done) {
actionContext.dispatch('ADD_ITEM', {name:'toto', key:new Date().getTime()});
});
}
render() {
return (
<div>
<button className='waves-effect waves-light btn' onClick={this.add.bind(this)}>Add</button>
<TodoTable listItems={this.state.listItem}></TodoTable>
</div>
);
}
}
export default Todo;
As a Fluxible user you should benefit from Fluxible addons:
connectToStores.
The following example will listen to changes in FooStore and BarStore and pass foo and bar as props to the Component when it is instantiated.
class Component extends React.Component {
render() {
return (
<ul>
<li>{this.props.foo}</li>
<li>{this.props.bar}</li>
</ul>
);
}
}
Component = connectToStores(Component, [FooStore, BarStore], (context, props) => ({
foo: context.getStore(FooStore).getFoo(),
bar: context.getStore(BarStore).getBar()
}));
export default Component;
Look into fluxible example for more details. Code exсerpt:
var connectToStores = require('fluxible-addons-react/connectToStores');
var TodoStore = require('../stores/TodoStore');
...
TodoApp = connectToStores(TodoApp, [TodoStore], function (context, props) {
return {
items: context.getStore(TodoStore).getAll()
};
});
As a result you wouldn't need to call setState, all store data will be in component's props.