setState/use State in external function react - javascript

Considering this pseudocode:
component.js
...
import {someFunc} from "./common_functions.js"
export default class MyComp extends Component {
constructor(props) {
super(props);
this.someFunc = someFunc.bind(this);
this.state = {...};
}
_anotherFunc = () = > {
....
this.someFunc();
}
render() {
...
}
}
common_functions.js
export function someFunc() {
if(this.state.whatever) {...}
this.setState{...}
}
How would I bind the function someFunc() to the context of the Component? I use it in various Components, so it makes sense to collect them in one file. Right now, I get the error "Cannot read whatever of undefined". The context of this is unknown...

You can't setState outside of the component because it is component's local state. If you need to update state which is shared, create a store (redux store).
In your case, you can define someFunction at one place and pass it the specific state variable(s) or entire state. After you are done in someFunction, return the modified state and update it back in your component using setState.
export function someFunc(state) {
if(state.whatever) {...}
const newState = { ...state, newValue: whateverValue }
return newState
}
_anotherFunc = () = > {
....
const newState = this.someFunc(this.state);
this.setState({newValue: newState});
}

it's not a React practice and it may cause lot of problems/bugs, but js allows to do it:
Module A:
export function your_external_func(thisObj, name, val) {
thisObj.setSate((prevState) => { // prevState - previous state
// do something with prevState ...
const newState = { // new state object
someData: `This is updated data ${ val }`,
[name]: val,
};
return newState
});
}
Then use it in your react-app module:
import { your_external_func } from '.../your_file_with_functions';
class YourReactComponent extends React.Component {
constructor(props, context) {
super(props, context);
this.state={
someName: '',
someData: '',
};
}
handleChange = (e) => {
const { target } = event;
const { name } = target;
const value = target.type === 'checkbox' ? target.checked : target.value;
your_external_func(this, name, value);
}
render() {
return (<span>
{ this.state.someData }
<br />
<input
name='someName'
value={ this.state.someName }
onChange={ this.handleChange }
/>
</span>);
}
}
It's a stupid example :) just to show you how you can do it

The best would obviously to use some kind of external library that manages this. As others have suggested, Redux and MobX are good for this. Using a high-order component to wrap all your other components is also an option.
However, here's an alternative solution to the ones above:
You could use a standard javascript class (not a React component) and pass in this to the function that you are calling from that class.
It's rather simple. I've created a simple example below where the state is changed from a function of another class; take a look:
class MyApp extends React.Component {
constructor() {
super();
this.state = {number: 1};
}
double = () => {
Global.myFunc(this);
}
render() {
return (
<div>
<p>{this.state.number}</p>
<button onClick={this.double}>Double up!</button>
</div>
);
}
}
class Global {
static myFunc = (t) => {
t.setState({number: t.state.number*2});
}
}
ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"><div>

There is a functional form of setState that can even be used outside of a component.
This is possible since the signature of setState is:
* #param {object|function} partialState Next partial state or function to
* produce next partial state to be merged with current state.
* #param {?function} callback Called after state is updated.
See Dan's tweet: https://twitter.com/dan_abramov/status/824308413559668744

This all depends on what you are trying to achieve. At first glance I can see 2 options for you. One create a child component and two: use redux as redux offers a singular state between all of your child components.
First option:
export default class parentClass extends Component {
state = {
param1: "hello".
};
render() {
return (
<Child param1={this.state.param1}/>
);
}
}
class Child extends Component {
render() {
console.log(this.props.param1);
return (
<h1>{this.props.param1}</h1>
);
}
}
Now the above child component will have the props.param1 defined from the props passed from it's parent render function.
The above would work but I can see you're trying to establish a 'common' set of functions. Option 2 sort of provides a way of doing that by creating a singular state for your app/project.
If you've haven't used redux before it's pretty simple to use once you've got the hang of it. I'll skip out the setup for now http://redux.js.org/docs/basics/UsageWithReact.html.
Make a reducer like so:
import * as config from './config';//I like to make a config file so it's easier to dispatch my actions etc
//const config.state = {param1: null}
//const config.SOME_FUNC = "test/SOME_FUNC";
export default function reducer(state = config.state, action = {}) {
switch(action.type) {
case config.SOME_FUNC:
return Object.assign({}, state, {
param1: action.param1,
});
break;
default:
return state;
}
}
}
Add that to your reducers for your store.
Wrap all your components in the Provider.
ReactDOM.render(
<Provider store={store} key="provider">
<App>
</Provider>,
element
);
Now you'll be able to use redux connect on all of the child components of the provider!
Like so:
import React, {Component} from 'react';
import {connect} from 'react-redux';
#connect(
state => (state),
dispatch => ({
someFunc: (param1) => dispatch({type: config.SOME_FUNC, param1: param1}),
})
)
export default class Child extends Component {
eventFunction = (event) => {
//if you wanted to update the store with a value from an input
this.props.someFunc(event.target.value);
}
render() {
return (
<h1>{this.props.test.param1}</h1>
);
}
}
When you get used to redux check this out https://github.com/redux-saga/redux-saga. This is your end goal! Sagas are great! If you get stuck let me know!

Parent component example where you define your callback and manage a global state :
export default class Parent extends Component {
constructor() {
super();
this.state = {
applyGlobalCss: false,
};
}
toggleCss() {
this.setState({ applyGlobalCss: !this.state.applyGlobalCss });
}
render() {
return (
<Child css={this.state.applyGlobalCss} onToggle={this.toggleCss} />
);
}
}
and then in child component you can use the props and callback like :
export default class Child extends Component {
render() {
console.log(this.props.css);
return (
<div onClick={this.props.onToggle}>
</div>
);
}
}
Child.propTypes = {
onToggle: PropTypes.func,
css: PropTypes.bool,
};

Well for your example I can see you can do this in a simpler way rather than passing anything.
Since you want to update the value of the state you can just return it from the function itself.
Just make the function you are using in your component async and wait for the function to return a value and set the state to that value.
import React from "react"
class MyApp extends React.Component {
constructor() {
super();
this.state = {number: 1};
}
theOnlyFunction = async() => {
const value = await someFunctionFromFile( // Pass Parameters );
if( value !== false ) // Just for your understanding I am writing this way
{
this.setState({ number: value })
}
}
render() {
return (
<div>
<p>{this.state.number}</p>
<button onClick={this.double}>Double up!</button>
</div>
);
}
}
And in SomeOtherFile.js
function someFunctionFromFile ( // catch params) {
if( //nah don't wanna do anything ) return false;
// and the blahh blahh algorithm
}

you should use react Context
Context lets us pass a value deep into the component tree without explicitly threading it through every component.
here is a use case from react docs : create a context for the current theme (with "light" as the default).
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
resource: https://reactjs.org/docs/context.html

Related

How to Programmatically Provide and Consume Context?

So my question is a simple one. In React js I want to pass some states and handlers from a parent to its 3rd grandchild using Context. I have implemented this within the jsx but I want to use the states within the javascript o that I have some logic before I completely output my states.
I have divided my question into 2 parts. 1.) What I have done so far. 2.) What I want to do essentially.
1.)
// this file just stores the Context
MyContext.js
import React, { Component } from 'react';
export const MyContext = React.createContext();
MyProvider.js // this class is used by the parent and the child to have access to the provider
import React, { Component } from 'react';
import {MyContext} from '../MyContext'
class MyProvider extends Component {
state = {
name: 'Wes',
age: 100,
cool: true
}
render() {
return (
<MyContext.Provider value={{
state: this.state,
growAYearOlder: () => this.setState({
age: this.state.age + 1
})
}}>
{this.props.children}
</MyContext.Provider>
)
}
}
export default MyProvider;
// Ok so now I am basically skipping the parent and showing you the consumer grand-child
Person.js
import React, { Component } from 'react';
// first we will make a new context
import { MyContext } from '../MyContext';
class Person extends Component {
render() {
return (
<div className="person">
<MyContext.Consumer>
{(context) => (
<React.Fragment>
<p>Age: {context.state.age}</p>
<p>Name: {context.state.name}</p>
<button onClick={context.growAYearOlder}>🍰🍥🎂</button>
</React.Fragment>
)}
</MyContext.Consumer>
</div>
)
}
}
export default Person;
2.)
// Ok so as you can see here I have had to immediately use the context.growAYearOlder. What I want to do instead is have control of it using javascript and modify it as desired; So something like this:
Child.js
const parentContext = MyContext.getContext();
if(somethingHappens){
parentContext().growAYearOlder();
}
return(
// The now rendered component
);
I tried something like this but it doesnt work:
MyContext.Consumer.context.growAYearOlder();
There are many similar questions with proper answers, docs, examples and so on - but this question kept popping up for me.
So, in case you want to get the context value and use it within your component's render() just import it (export context itself not only provider) and use _currentValue e.g.
const contextData = MyContext._currentValue;
Note that you still have to wrap your components with your given context provider.
Also note that for function components, you need to use useContext e.g.
const contextData = useContext(MyContext);
And for class components you can assign the context to a static var and then use it e.g.
class Main extends React.Component(){
static contextType = MyContext;
componentDidMount(){
const contextData = this.context;
}
render() {
return (
<p>Hey</p>
);
}
Note that the static var has to be called contextType otherwise this.context won't hold the MyContext data.
I've based my answer solely from the docs itself(https://reactjs.org/docs/context.html#updating-context-from-a-nested-component)
import React, { Component } from 'react';
import { MyContext } from '../MyContext'
class MyProvider extends Component {
constructor(props) {
super(props)
// I've moved the state declaration inside the constructor
this.state = {
name: 'Wes',
age: 100,
cool: true
}
// moved the function here and added prevState
this.growAYearOlder = () => {
this.setState(prevState => ({
age: prevState.age + 1,
}))
};
}
render() {
return (
<MyContext.Provider value={{
state: this.state,
growAYearOlder: this.growAYearOlder,
}}>
{this.props.children}
</MyContext.Provider>
)
}
}
export default MyProvider;

Change props value outsite react and reload component

As the title says, I want to change value of props and reload component in external js file.
<div data-group=""></div>
//external.js
const popupChat = document.getElementById('popupChatComponent');
popupChat.setAttribute('data-group', groupId);
//component.ts
export default class PopupChatRoot extends React.Component {
private readonly groupId: string;
constructor(props) {
super(props);
this.groupId = this.props.group;
}
render() {
return (
<div className="modal-body">
<p>{this.groupId}</p>
</div>
);
}
}
const component = document.getElementById('popupChatComponent');
if (component) {
const props = Object.assign({}, component!.dataset);
render(<PopupChatRoot {...props}/>, component);
}
How I can do this ?
What you can do is use a wrapper component or higher order component which provides those props to your component, and have that have that wrapper component integrated with your external javascript code.
Here is an HOC I use to do something similar:
export interface MyHocProps {
//the props you want to provide to your component
myProp: any;
}
export const withMyHOC = <T extends any>(params: any) =>
<P extends MyHocProps>(WrappedComponent: React.ComponentType<P>): React.ComponentClass<defs.Omit<P, keyof MyHocProps>> => {
return class extends React.PureComponent<defs.Omit<P, keyof MyHocProps>> {
//here you have access to params, which can contain anything you want
// maybe you can provide some sort of observable which causes this to re-render
render() {
return <WrappedComponent
{...this.props}
myProp={/*whatever*/}
/>;
}
}
};
From here, you would integrate this HOC with some kind of system to push changes to it. I recommend using an observable. Basically you want to have this HOC component subscribe to changes in some piece of observable data, and then force itself to re-render when it changes.
Alternatively, you can just expose some method on your component if it is just a singleton by doing something like window.reloadThisComponent = this.reload.bind(this);, but that should probably be considered a last resort.
It is just a generic example, it might help you to solve your problem. Actually I don't think you can change props of the root node.
// yourEventService.js
class YourEventService {
listeners = []
subscribe = listener => this.listeners.push(listener)
unsubscribe = listener => this.listeners = this.listeners.filter(item => item !== listener)
emit = message => listener.forEach(listener => listener(message))
}
export default new YourEventService() // singleton export
// someWhereElse.js
import yourEventService from './yourEventService'
window.addEventListener('click', () => yourEventService.emit('myNewGroup')) // it's just an event example
//component.js, sorry I don't know how to typescript well
import yourEventService from './yourEventService'
export default class PopupChatRoot extends React.Component {
state = {
groupId: this.props.group; // initial value is passed by props
}
componentDidMount() {
yourEventService.subscribe(this.handleMessage)
}
componentWillUnmount() {
yourEventService.unsubscribe(this.handleMessage)
}
handleMessage = message => {
this.setState({ groupId: message })
}
render() {
return (
<div className="modal-body">
<p>{this.state.groupId}</p>
</div>
);
}
}
const component = document.getElementById('popupChatComponent');
if (component) {
const props = Object.assign({}, component.dataset);
render(<PopupChatRoot {...props}/>, component);
}

React: parent component props in child without passing explicitly

Is it possible to have the props of the parent component to be available in child component without passing them down?
I am trying to implement a provider pattern, so that to access all the provider props in its child components.
EX:
Suppose the below provider comp FetchProvider will fetch the data and theme props on its own, and when any child component is enclosed by it, I want to access both props "data" and "theme" in the child component as well. How can we achieve it?
class FetchProvider
{
proptypes= {
data: PropTypes.shape({}),
theme: PropTypes.shape({})
}
render()
{
// do some
}
mapStateToProps()
{
return {data, theme};
}
}
class ChildComponent
{
proptypes= {
name: PropTypes.shape({})
}
render()
{
const{data, them} = this.props; // is this possible here?
// do some
}
}
and if I try to above components as below.
<FetchProvider>
<ChildComponent name="some value"/> //how can we access parent component props here? without passing them down
<FetchProvider/>
This is exactly what react context is all about.
A Consumer can access data the a Provider exposes no matter how deeply nested it is.
// Context lets us pass a value deep into the component tree
// without explicitly threading it through every component.
// Create a context for the current theme (with "light" as the default).
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton(props) {
// Use a Consumer to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
return (
<ThemeContext.Consumer>
{theme => <Button {...props} theme={theme} />}
</ThemeContext.Consumer>
);
}
Here is a small running example:
Note This is the react v16 context API.
Your use case can be solved with the usage of React context. With the help of Context, any child that is wrapped by a provided can be a consumer for the data that is provided by the Provider
In your case, you can use it like
context.js
export const FetchContext = React.createContext();
Provider.js
import { FetchContext } from 'path/to/context.js';
class FetchProvider extends React.Component
{
proptypes= {
data: PropTypes.shape({}),
theme: PropTypes.shape({})
}
render()
{
const { data, theme, children } = this.props;
return (
<FetchContext.Provider value={{ data, theme}}>
{children}
</FetchContext.Provider>
)
}
mapStateToProps()
{
return {data, theme};
}
}
ChildComponent.js
class ChildComponent extends React.Component
{
proptypes= {
name: PropTypes.shape({})
}
render()
{
const{data, them} = this.props; // use it from props here
// do some
}
}
export default (props) => (
<FetchContext.Consumer>
{({ data, theme }) => <ChildComponent {...props} data={data} theme={theme} />}
</FetchContext.Consumer>
)
However given the fact that you are already using Redux, which is build on the concept of Context, you might as well use redux and access the values within the child component since they are the same values that are supplied from the Redux store to the child by parent.
class ChildComponent extends React.Component
{
proptypes= {
name: PropTypes.shape({})
}
render()
{
const{data, them} = this.props; // use it from props here
// do some
}
}
const mapStateToProps = (state) => {
return {
data: state.data,
theme: state.theme
}
}
You can use React.Children to iterate over the children and pass whatever props you want to send to the new cloned elements using React.cloneElement.
EX:
class Parent extends React.Component {
constructor(props) {
super(props);
}
render() {
const { children } = this.props;
const newChildren = React.Children.map(children, child =>
React.cloneElement(child, { myProp: 'test' }));
return(
<View>
{newChildren}
</View>
)
}
}
Are you looking for:
class MyParent extends Component {
render() {
return <MyChild {...this.props}>
// child components
</MyChild>
}
}
This would pass all of the props passed into MyParent to the MyChild being rendered.

React HOC to handle clicks outside a given Component

I desire to make a Higher Order Component to manage outside clicks. When it is determined that a user has clicked outside a given component a certain passed function should execute. This HOC takes 2 parameters:
BoundaryComponent: the component we are interested in determining if clicks are outside of it
onOutsideClick: a function to execute when a click occurs outside the component
This ClickOutside component looks like:
import React from 'react'
const { Component } = React
import { findDOMNode } from 'react-dom'
export default function ClickOutside (BoundaryComponent, onOutsideClick) {
return class Wrapper extends Component {
constructor (props) {
super(props)
}
componentDidMount() {
document.addEventListener('click', this.handleClick.bind(this), true)
}
componentWillUnmount() {
document.removeEventListener('click', this.handleClick.bind(this), true)
}
render () {
const props = Object.assign({}, this.props, { ref: this.getContainer.bind(this) })
return (
<BoundaryComponent
{...props}
/>
)
}
getContainer (wrapped) {
this.container = findDOMNode(wrapped)
}
handleClick(e) {
if (this.container && !this.container.contains(e.target) && typeof onOutsideClick === 'function') {
onOutsideClick()
}
}
}
}
And I am trying to utilize this component like this:
import React from 'react'
const { ClickOutside } = 'utils/click-outside'
import { updatePicklistActiveIndex } from 'components/store/actions'
import { getPicklistActiveIndex } from 'components/store/selectors'
import PickList from 'components/picklist'
// ...
function mapDispatchToProps (dispatch) {
return {
updatePicklistActiveIndex: (activeIndex) => { dispatch(updatePicklistActiveIndex(activeIndex)) },
}
}
function mapStateToProps (state) {
return {
picklistActiveIndex: getPicklistActiveIndex(state),
}
}
let onOutsideClick = null // This feels like a code smell
class DropdownPickList extends PickList {
constructor(props) {
super(props)
this.state = {
dropdownVisible: false,
}
onOutsideClick = () => {
this.props.updatePicklistActiveIndex(-1)
this.setState({ dropdownVisible: false })
}
}
// ...
render() {
return (
//...jsx to render DropdownPickList
)
}
}
const pickList = ClickOutside(DropdownPickList, () => { onOutsideClick() })
export default connect(mapStateToProps, mapDispatchToProps)(pickList)
The above implementation works as I would expect...
But defining onOutsideClick outside the context of the DropdownPickList Class, and then overwriting this variable reference within the constructor of this Class just feels wrong. But it seems to be the only way to get access to the props and state within the class needed to close the dropdown.
I also tried using a static method in the DropdownPickList class to handle the onOutsideClick, but the problem with this is this Class is passed as an unrendered component, so I do not have access to any of its instance methods in my handleClick of the HOC.
There must be a better way to do this? Any help or alternative implementation ideas/patterns would be greatly appreciated!
Instead of creating a function, just export the wrapper and change as the following :
BoundaryComponent : move to be the children of the wrapper (so it will be actually a wrapper) like so :
<Wrapper>
<BoundaryComponent />
<Wrapper>
While inside wrapper you will render {this.props.children}
onOutsideClick: the onOutsideClick function move to the props of Wrapper :
<Wrapper onOutsideClick={this.onOutsideClick}>
<BoundaryComponent />
<Wrapper>

Reactjs, parent component, state and props

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.

Categories

Resources