If I simply have:
const App = function() {
return (
<div>{this.renderList()}</div>
)
}
How do I define the renderList method?
I can't do const renderList = function() {} (nor with var or let). I can't do renderList() {}.
What's the right syntax?
I am hesitant to give a solution to this because inline Stateless Functions are not supposed to have methods. if you want a method you should use a Class and theres nothing wrong with using a class. Its all based on what you need to do. Stateless Functions are designed to be a super light weight way to render something that doesn't need methods, or a state or even a this context (in terms of a class).
you should be doing it like this.
class App extends Component {
constructor(){
super();
// note this is a Stateless Component because its a react class without a state defined.
}
renderList = () => {
return <span>Something Here</span>;
}
render() {
return <div>{this.renderList()}</div>
}
}
a HACK way that I wouldn't recommend (but does solve your question in the way you want it to) would be like this.
const App = () => {
let renderList = () => {
return <span>Something Here</span>
}
return <div>{renderList()}</div>
}
The reason why its generally a bad practice is because you are creating a function and all the memory allocation needed every render cycle. Subsequently, the internal diffing optimizations that react provides is generally useless if you do this because a new function gives a different signature than the prior render cycle. If this had a lot of children, they would all be forced to re-render!
Edit - React Version 16.8.0 +
You can use Hooks to do this. I would recommend using memo to memoize the function, this way you aren't creating it in memory each render cycle.
const RenderList = React.memo(props => (
<span>Something Here</span>
))
const App = function() {
const renderList = ()=> {
return "this variables"
}
return (
<div>{renderList()}</div>
)
}
You would want to do something like this
const App = function() {
return (
<div>{renderList()}</div>
)
}
function renderList(){
return "this variables"
}
Naturally this is a bad approach you its recommended that you pass in functions as props and stateless component are always dumb componets. Say if you are using redux for example you can have your component render like this
import {connect} from 'react-redux';
const App = (props) => {
return (
<div> {props.renderList} </div>
)
}
function renderList (){
return "your render logic"
}
export default connect(null, {renderList})(App)
Can you try something like
const App = () => {
return (
<div>{this.renderList()}</div>
)
}
App.renderList = () => {
return 'This is my list'
}
You can create render list function as standalone and use function parameter to pass props into function.
Related
I'm working on a React Typescript project. A very simplified version of the project is below. I'm trying to use more traditional polymorphism here where I have components returned from vanilla Typescript objects (not React components) that are rendered in the component tree. The reason I want to do this is so that I can have polymorphic classes that I change at runtime and that manage their own state and business logic.
import React, { useEffect } from "react";
class ClickCounter {
private count: number;
constructor() {
this.count = 0;
}
IncrementCount() {
this.count += 1;
}
GetCount(): number {
return this.count;
}
}
interface Operation {
HandleMouseDown(event: React.MouseEvent<HTMLDivElement>): void;
GetComponents(): JSX.Element[];
}
class ClickCounterOperation implements Operation {
private clickCounter: ClickCounter;
constructor() {
const counter: ClickCounter = new ClickCounter();
this.clickCounter = counter;
}
HandleMouseDown(_: React.MouseEvent<HTMLDivElement>): void {
this.clickCounter.IncrementCount();
}
GetComponents(): JSX.Element[] {
const count: number = this.clickCounter.GetCount();
return [<div>you have clicked {count} times</div>];
}
}
export type AppState = {
currentOperation: Operation;
};
export class App extends React.Component<{}, AppState> {
constructor(props = {}) {
super(props);
const initialOperation: Operation = new ClickCounterOperation();
this.state = {
currentOperation: initialOperation,
};
this.HandleMouseDown = this.HandleMouseDown.bind(this);
}
HandleMouseDown(event: React.MouseEvent<HTMLDivElement>) {
console.log("Dispatching mouse down event to current operation");
this.state.currentOperation.HandleMouseDown(event);
}
render() {
return (
<div className="App" onMouseDown={this.HandleMouseDown}>
{this.state.currentOperation.GetComponents()}
<div>some other stuff to show</div>
</div>
);
}
}
export default App;
In the example above everything will render initially, but not after the count is updated. This is because react has no way of knowing that the state has changed and that a rerender is needed. What I'm currently doing is forcing React to rerender by passing down a RefreshOperationState callback to the Operation object that will call the App.setState() method but this feels very ugly and I don't want to do this.
Any way to achieve this kind of traditional polymorphism with React and have non-React objects inject components into the component tree and have the components update when appropriate? I understand what I am trying to do is not following the common React patterns of using Flux/Redux and having all/most app state in a store/s, but I'd like to make this app in a less functional and more OOP pattern where objects store their own state and are called polymorphicly.
Any suggestions?
As you've noted, mixing paradigms might be more trouble than it's worth. React relies on object reference equality to handle its rendering logic. Since you're mutating objects instead of creating new ones, it will never know to update.
Another rule of React state is that it is only data (never behavior and definitely not JSX), and you're trying to use both.
You could make components which use hooks like these, and then let your parent component choose how it composes itself based on what kind of Operation you want.
const useClickCounter = () => {
const [count, setCount] = useState(0);
const incCount = setCount(count + 1);
return [incCount, count];
};
The only other thing I've done is use the observable pattern on the class objects and have a React context in between which observes them and sends the updated state into the React component. The React context provider will cause all consumers beneath it to rerender with fresh state.
public subscribe = (fn: (state) => void) => {
this.observers.push(fn);
}
private update = async () => {
// Give new state to the Context which is subscribed
this.observers.forEach(fn => fn(state));
}
PS: if you're familiar with Redux, you could start with something like this:
const ClickCounter = () => {
const value = useSelector(selectedClickCounter);
return <div>{value}</div>;
};
const operations = {
clickCounter: {
RenderComponent: ClickCounter,
onPressDispatchData: { type: "increment-counter" },
},
};
const OperationHandler = () => {
const [currentOperation, setCurrentOperation] = useState(operations.clickCounter);
return <HandleMouse {...currentOperation} />;
};
const HandleMouse = (props) => {
return (
<div className="App" onMouseDown={props.onPressDispatchData}>
{props.RenderComponent}
<div>some other stuff to show</div>
</div>
);
};
I am trying to figure out how to implement my own custom events. I asked the question here but the word event seems to confuse my question. I was asked to add a new question, so I will try to do my best in another way:
Related post
My component:
import React, { useState } from "react";
const DropdownPaging2 = props => {
function myClickFunc(val) {
alert("WHAT SHOULD I ADD HERE TO FIRE MY EVENT TO THE CONSUMING COMPONENT");
}
return <div onClick={() => myClickFunc(100)}>CLICK me</div>;
};
export default DropdownPaging2;
Using my component in another components (comsuming component) render function:
<DropdownPaging2></DropdownPaging2>
I would like implement so I can pass a new event to the consuming component. Something lige this:
<DropdownPaging2 myCustomEvent={() => myCustomEvent(100)}></DropdownPaging2>
You can pass functions as props to your DropdownPaging2 component like you mentioned:
<DropdownPaging2 myEvent={() => myCustomEvent(100)}></DropdownPaging2>
And then use it in the component like this.
const DropdownPaging2 = props => {
const myClickFunc = (val) => {
if(props.myEvent){
props.myEvent();
} else {
// default if no function is passed
}
}
return <div onClick={() => myClickFunc(100)}>CLICK me</div>;
};
export default DropdownPaging2;
This way you are free to pass a custom function
Just make your component use the custom callback if it was passed as a prob, otherwise use the default one.
return <div onClick={prop.myCustomEvent ? prop.myCustomEvent : () => myClickFunc(100)}>CLICK me</div>;
So I have something like the following:
function calculate = (value) => { return value + somecalculations }
class MyComponent extends React.Component {
...
render() {
if (calcuate(this.props.value) === 1) {
return(<MyComponentVersion1 />)
} else {
return <MyComponentVersion2 />
}
}
}
My question is, when doing jest unit testing, I want to be able to mock the function calculate(). But the function is global to that file, and is not part of my react component. Is there a way to mock this function so it always returns say 1? Thanks
If you want to do this without any extra dependencies (like a mocking library), you should be able to use dependency injection by telling MyComponent which function to use by setting it in a prop on your component to achieve this, like so:
calculate = (value) => { return value + somecalculations }
class MyComponent extends React.Component {
constructor(props) {
this.calculate = this.props.calculate || calculate
}
render() {
if (this.calculate(this.props.value) === 1 {
return (<MyComponentVersion1 />)
} else {
return (<MyComponentVersion2 />)
}
}
}
...and then, in your test, you can use a mock-calculate function:
test('put a really good test description here', () => {
const mockCalculate = () => 1
const myTestSubject = (<MyComponent calculate={mockCalculate} value={whatever}/>)
// the rest of your test
})
If you want to use an actual mocking library instead, maybe try sinon.js Mocks.
You need a way to access the calculate function from outside of the file. The easiest way to do this would be to export the function separately:
export function calculate () {
// ...
}
This option is also minimally invasive to your source code.
export function injectProps() {
const injects = {store: new Store()}; // some store
return function (Component) {
return class Proxy extends React.Component {
render() {
return React.createElement(Component, {
...injects,
...this.props,
});
}
};
}
}
Is it ok to use this instead of Redux or Context API with React?
Update: I think I missed to point out my expectation. I'm actually passing some service(http, localStorage) to childrens only when they asks for it. It's not only about the store as services don't have any state. But I also need to pass store through it.
https://pastebin.com/G3PgVxLn
Maybe this tweet by the Dan Abramov (React maintainer) might help.
I understand it was probably not the point of the article. But I see
people reaching for Context or Redux because they don’t realize
components can take any children — and that often removes the need for
deep prop passing. Would be great to highlight!
And Dave Ceddia posted a relavant React documentation link.
Composition vs Inheritance
You can read upon those two.
And here is a demo Nicolas Marcora created to show me how to pass properties to child/children.
You can pass props to children using React.cloneElement(child,...
Working demo on StackBlitz.
export default class WithMouse extends React.Component {
state = { x: 0, y: 0 }
handleMouseMove = event => { ... }
render() {
const { children } = this.props
const childElements = React.Children.map(children, child =>
React.cloneElement(child, {
mouse: this.state,
onMouseMove: this.handleMouseMove
})
)
return <div>
{ childElements }
</div>
}
}
You can use WithMouse class to pass props downward to all children and use it like following.
class App extends Component {
...
render() {
return (
<WithMouse>
<MouseTracker />
</WithMouse>
);
}
}
MouseTracker has access to props passed from WithMouse so you can just use it without directly passing it manually.
You can probably go further and pass all props instead of a few (mouse, onMouseMove)
Looking at this simple example where the prop toggleData would be a redux thunk action mapped to the container props.
Is this the recommended way to pass a function like this to a child 'dumb' component? I read an article online saying that using arrow functions inside handlers is expensive and not very efficient from a performance perspective.
class Container extends React.Component {
render(){
return (
<Home toggleData={this.props.toggleData}/>
)
}
}
const Home = (props) => {
return (
<button onClick={()=>{props.toggleData()}}></button>
)
}
Yes! Avoid using arrow functions inside the JSX. Because the function will be created on every render, this might lead to performance issues later on.
If you don't need to send parameters you can do something like this:
const Home = (props) => {
return (
<button onClick={props.toggleData}></button>
)
}
If you need to use parameters, I usually define a class to create the callback using an arrow function, this way it gets created and bound only once.
class Home extends PureComponent {
onToggle = () => {
this.props.toggleData(1, 2, 3);
}
render() {
return (
<button onClick={this.onToggle}></button>
)
}
}