When you set a component or element callback for an event, tutorials and documentation show code like this:
'use strict';
import React from 'react';
let FooComponent = React.createClass({
handleClick(args) {
...
},
render() {
return <div>
<h1>Some title</h1>
<button onClick={this.handleClick}>Click Me!</button>
</div>
}
};
export default FooComponent;
But this handleClick method can be accessed out of this component, if I'd use FooComponent on another component and assign it a reference I can access the handleClick from this other component.
'use strict';
import React from 'react';
import FooComponent from './FooComponent';
let BarComponent = React.createClass(
handleBarComponentClick(e) {
this.refs.fooComponent.handleClick(null);
},
render() {
return <div>
<FooComponent ref="fooComponent" />
<button onClick={this.handleBarComponentClick}>Other click</button>
</div>
}
);
export default BarComponent;
I don't like the fact that I can access that method, which in my opinion should be private or maybe I don't have to worry about it. But to fix that I started using this "good/bad practice" in my projects to avoid that method from being accessed.
'use strict';
import React from 'react';
function handleClick(args) {
...
}
let FooComponent = React.createClass({
render() {
return <div>
<h1>Some title</h1>
<button onClick={handleClick.bind(this)}>Click Me!</button>
</div>
}
};
export default FooComponent;
So it cannot be accessed from outside components.
My doubt is, if what I'm doing is a good or bad practice, or what could be the problems that could happen or not if I continue doing this? Or maybe I don't have to worry to set the event handlers inside the createClass argument?
Thanks in advance :)
Have you checked the Flux pattern? https://facebook.github.io/react/docs/flux-overview.html
In my React apps, this is not a concern. Although I do not define event handlers in a private way, the general rule is that you NEVER call a method on a component. If the subcomponent needs to notify its parent of something, this is accomplished either by a callback transferred from parent to child as a prop or by mutating a global state (via an action) in the child component. If, on the other hand, is the parent that needs to accomplish something on the child, then it changes the props (or the values of such props) on the subcomponent.
Trying to answer your question, I'd say that what you are doing right now (defining the event handlers in a private scope) is ok. But I think is more of a hassle to do such a thing for every handler. I would suggest you to review if the general architecture of your app is in line with what React suggests.
Related
I came up with a mind-boggling situation, which did not (at least yet) cause any problems, but I couldn't find anything by googling.
A theoretical (and unrealistic but illustrative) example of two React Components:
// parent.js
import Child from "./path-to-child";
export function Parent(props) {
return(
<Child content="some content" />
);
}
// child.js
import withContext from "./path-to-context";
function Child(props) {
return(
<p>
{props.content}
</p>
);
}
export default withContext(Child);
So the Parent is passing content as a prop to Child. If the context provided by withContext HOC also happened to have a property with name content, what would happen? Is there an order of precedence or is content just the latest value which happens to overwrite the older one or perhaps something else?
You can get, reference here in this StackBlitz Link
I have one folder file todoState.ts in models folder of project. This todoState.ts used for managing global state of application using custom hooks. I need to perform following tasks on global todoState.ts..
Add new State
Remove state
update state
here is code of todoState.ts.
import { useState, useEffect } from "react";
const todoState = (todo?, callback?) => {
const [todos, setTodos] = useState([todo]);
useEffect(()=>{
setTodos(todo);
},[])
return [{todos, setTodos}];
}
Now, I have Two user defined components which are dependent on globalState. which are 1. <TodoForm /> and <TodoListLineItem />
Both of above component is rendered inside index.tsx.
How can I manage global state from all the components including index.tsx , <TodoForm /> , <TodoListLineItem />. Here...
<TodoForm /> , is used to add to tasks to list.
<TodoListLineItem />, is used to display all added tasks from TodoForm component. when user hover over to each todo list item then user can able to remove perticular tasks-item from global management state.
Which one is best and reusable way to implement this global management state feature?
How one component changed state from A to B is reflected on related component by just manipulating global state object of application. If I put useState([]) into index.tsx then it will works well, But I want to manage state from TodoState.ts file. Thank You.
export default todoState;
Finally , I found and learned New concepts for React-Context API. You can checkout here StackBlitz Link
To manage global states React provides context-api. Use only when you have multiple level of component properties pass down in component tree from Top to bottom. I used very small example to understand context api for my different use-case.
First of all we need to use two context..
createContext [ used to create global state context ]
useContext [ used to get states from context from child component ].
To work with Context I created one <context.Provider> component. and all state management tasks are done with only this component level only. all child component just send events of what to do. and global context of provider component changes accordingly.
firstly, Create context..
export interface ItodoContext{
todoState? : Itodo[];
addNewTodoState?: (state?: string) => void;
removeTodoItemByIndex? : (index?: number) => void;
}
export const todoContext = createContext<ItodoContext[]>([{}]);
As, I am using React-Typescript functional component, as per Interface of context i defined all tasks of states. I passed all TodoState, function to manipulate TodoState like addNewTodoState, removeTodoItemByIndex.
then create Provider of context..
const allTodoStates = {
todoState,
addNewTodoState,
removeTodoItemByIndex
}
return(
<todoContext.Provider value = { [allTodoStates]}>
{props.children}
</todoContext.Provider>
)
Then I set Provider as parent component in tree in index.tsx component like this..
const App: FC = () => {
return (
<div className="h-100 w-100">
<TodoStateProvider>
<Navbar />
<div className="container">
<TodoForm />
<TodoListLineItem />
</div>
</TodoStateProvider>
</div>
);
}
See above all components now child of <TodoStateProvider> parent component.
When I need to add new state to context is inside <TodoForm> and how we can add state is as below code...
const [{addNewTodoState}] = useContext(todoContext);
const sendTodoItem = (e) => {
addNewTodoState(todoInput.trim());
}
and so on.. provider component has value property, and we can get those properties from child using useContext() hook. as we used above. See full working demo I have attached in above StackBlitz Link.
What are some of the best practices to get access to data from other functions to my React Component?
I have a very simple component as :
/* eslint-disable */
import React from 'react'
import { test } from './FunctionTest'
function Component() {
return (
<div>
<h1>This is from Component</h1>
<p>{test()}</p>
</div>
)
}
export default Component
I am importing a function called test from a file called FunctionTest. The file is as follows :
export function test() {
let a = 2
return a
}
My goal is to access the data a from which is returning from FunctionTest file, to my component.js file. Clearly, by doing <p>{test()}</p> is totally not the right way.
I would like to ask what are best practices to do this, and how does it relate to props as FunctionTest is simply a JS function and not a React Component
I have seen other answers over here, but they are much more complicated, so in case of closing this question, please make sure I am getting pointed to the right resources.
Any help would be great.
Thank you for reading!
I want to invoke the function good without calling it from a event. It should run as soon as page opened just like in the self invoking javascript function.
Here is an example
import React from 'react';
class App extends React.Component {
good(){
console.log('I was triggered during good')
}
render() {
console.log('I was triggered during render')
return(
<div>
good();
</div>
);
}
}
export default App;
Few Points:
1. You need to use this keyword to call any function from any other function.
2. To put js code inside JSX, we need to use {}.
Write it like this:
import React from 'react';
class App extends React.Component {
good(){
console.log('I was triggered during good')
return <div> Hello </div>
}
render() {
console.log('I was triggered during render')
return(
<div>
{this.good()}
</div>
);
}
}
Check React DOC: https://facebook.github.io/react/docs/introducing-jsx.html
Check these answers for more details:
How does the "this" keyword work?
What do curly braces mean in JSX (React)?
You can also use lifecycle methods as componentDidMount(){} or componentWillMount(){}.
componentWillMount will be triggered before mounting of component and componentDidMount() will be triggered after component has been mounted.
I am learning to test React stateless components using the ReactTestUtils library. This is my simple component:
import React from 'react';
const Greeter = ({name,place}) => (
<h1>Hello,{name}. Welcome to the {place}.</h1>
);
export default Greeter;
This is my test spec, to get the renderIntoDocument working, I wrapped my Greeter component in a div as suggested here:
import {expect} from 'chai';
import React from 'react';
import ReactTestUtils from 'react-addons-test-utils';
import Greeter from '../Greeter';
describe('Greeter Components',() => {
it('renders correctly',() => {
var component = ReactTestUtils.renderIntoDocument(<div>
<Greeter name="Vamsi" place="Hotel California"/>
</div>);
var hasH1 = ReactTestUtils.findRenderedDOMComponentWithTag(component,'h1');
expect(hasH1).to.be.ok;
});
});
I get the error
findAllInRenderedTree(...): instance must be a composite component.
I am providing my code as jsbin here.
Since function components don't have an instance associated with them, you can't use them directly with render or renderIntoDocument. Attempting to wrap the function component is a good idea, unfortunately using a div doesn't work for a similar reason. DOM components also don't return a component instance, instead they return the underlying DOM node.
Which is all to say that you can't use the test utils function or native components as the "root" component you are rendering. Instead you will want to wrap your function components in a wrapper component that uses createClass or extends React.Component.
class Wrapper extends React.Component {
render() {
return this.props.children
}
}
let component = renderIntoDocument(<Wrapper><Greeter /></wrapper>
Gotcha's like this may be reason enough to make use of a third-party testing library like the popular enzyme, or my own take: teaspoon. Both abstract over issues like this by seamlessly wrapping and unwrapping function components for you, so you don't need to worry about what type of component you are trying to render.
Wrapping functional components in a <div> works for me. You just have to search for the component you want to test a little differently, i.e.
const props = { p1: "1" }
test('Foo renders correctly classed div', () => {
const cpt = TestUtils.renderIntoDocument(<div><Foo {...props} /></div>);
const myNode = ReactDOM.findDOMNode(cpt.childNodes[0]);
expect(myNode.className).toBe('my-class');
});
notice that you can target myNode for testing using cpt.childNodes[0]
In order to improve #monastic-panic's answer, my two cents:
You don't have to create a class for that. Do it dynamically:
import createReactClass from 'create-react-class';
// need to be a class component
const Clazz = createReactClass({
render: () => {
return <YourFunctionalComponentName {...props} />;
},
});
ReactTestUtils.renderIntoDocument(<Clazz />);