Simple passing of Value from one Component to another through context - reactjs

I'm new to react native and would like to use Context to keep a socket connection alive between screens in the future. For now, I tried to learn the concept of context just to pass simple values around but the value doesn't get sent.
Tried to follow the tutorial here, but by sending simple values instead.
I create my ValueContext in ValueContext.js here.
import React from 'react';
const ValueContext = React.createContext();
export default ValueContext;
Here's my LoginScreen.js where I set context provider.
import React, { Component } from 'react';
import ConnectionScreen from './ConnectionScreen';
import ValueContext from './ValueContext';
const testValue = 5;
export const sendValue = props => (
<ValueContext.Provider value={testValue}>
<ConnectionScreen />
</ValueContext.Provider>
)
class LoginScreen extends Component {
render() {
return()
}
}
Then in my ConnectionScreen.js
import React, { Component } from 'react';
import { View, Alert } from 'react-native';
import LoginScreen from './LoginScreen';
import ValueContext from './ValueContext';
export const receiveValue = props => (
<ValueContext.Consumer>
{testValue => <ConnectionScreen {...props} testValue={testValue} />}
</ValueContext.Consumer>
)
class ConnectionScreen extends Component {
showAlertValue = () => {
Alert.alert(this.props.testValue);
}
render() {
return(
<View>
{this.showAlertValue()}
</View>
)
}
}
So after setting the value in LoginScreen, I would like to access it in ConnectionScreen. All I get in my alert box is an empty box with no values. Am I doing something wrong here?

Related

How use Mobx 6 storage with React 17?

I'm completely confused with the rules of mobX - react . Some methods in another project work, but not in this test.
Below is the code of my components from the test application
App.js
import React, { FC } from 'react';
import "./App.css";
import {observer} from 'mobx-react';
import ComponentFirst from './components/ComponentFirst/ComponentFirst';
import ComponentSecond from './components/ComponentSecond/ComponentSecond';
const App: FC = observer(() => {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<ComponentFirst />
<ComponentSecond />
</div>
);
})
export default App;
ComponentFirst
import React, { FC } from 'react';
import {testStoreFirst} from '../../stores/testStoreFirst';
const ComponentFirst : FC = () => {
return (
<div>
<h3>It is First Component</h3>
<p>{testStoreFirst.testText}</p>
<p>{testStoreFirst.textForSecondTestStore}</p>
<button onClick={() => {testStoreFirst.setTestText('New text after click')}}>Click me!!!</button>
</div>
)
}
export default ComponentFirst;
ComponentSecond
import React, { FC } from 'react';
import {testStoreSecond} from '../../stores/testStoreSecond';
import {testStoreFirst} from '../../stores/testStoreFirst';
const ComponentSecond : FC = () => {
return (
<div>
<h3>It is Second Component</h3>
<p>{testStoreSecond.textFromFirstStore}</p>
<button onClick={() =>{testStoreFirst.setTextForSecondTestStore('I can change text from second storage')}}>Click me!!!</button>
</div>
)
}
export default ComponentSecond;
testStoreFirst
import { makeAutoObservable} from "mobx";
class TestStoreFirst {
testText='It is test text from mobX storage';
textForSecondTestStore='this text from First Store!!!';
constructor() {
makeAutoObservable(this);
}
setTextForSecondTestStore = (newText : string) => {
this.textForSecondTestStore = newText;
}
setTestText = (newText: string) => {
this.testText = newText;
console.log('It is not work');
}
}
export const testStoreFirst = new TestStoreFirst()
testStoreSecond
import {makeAutoObservable} from 'mobx'
import {testStoreFirst} from './testStoreFirst'
class TestStoreSecond {
textFromFirstStore = testStoreFirst.textForSecondTestStore
constructor() {
makeAutoObservable(this);
}
}
export const testStoreSecond = new TestStoreSecond();
My question
My App component is subscribed via observe to changes in stores. By clicking on the first button, in 1 component the text in the storage and, accordingly, the text should change, but it does not change. And In the second component, the value for the text field is taken from testStoreSecond. There, the text field is taken from testStoreFirst . When the button is clicked, the method from testStoreFirst is executed which should change the text, but it does not change.
I've read the documentation, but I still don't fully understand how to use the store and achieve "reactivity" and instant change of the component.
You need to wrap every component that uses any observable values with observer decorator, like you did with an App. But in case of App it's actually useless because you are not using observable values there. So just wrap other components and it should work fine.
As for this line textFromFirstStore = testStoreFirst.textForSecondTestStore it won't work like you expected because you just assigned value of testStoreFirst.textForSecondTestStore to textFromFirstStore and that's it.
To make such value reactive you need to use computed value. To make computed you just need to setup a getter function, like that:
class TestStoreSecond {
// ...
get textFromFirstStore() {
return testStoreFirst.textForSecondTestStore
}
// ...
}
// And in React access it just as before (it's a getter, not a function)
<p>{testStoreSecond.textFromFirstStore}</p>

How to render a notification from a global function - React

I'm new to React and I am trying to utilize notistack for ReactJs and I would like to display the notification by calling a helper function but I'm not quite sure how to do that. Here is the standard code required to use the component:
App component:
import { SnackbarProvider } from 'notistack';
<SnackbarProvider maxSnack={3}>
<App />
</SnackbarProvider>
Component that displays the notification:
import { withSnackbar } from 'notistack';
class MyComponent extends Component {
handleNetworkRequest = () => {
fetchSomeData()
.then(() => this.props.enqueueSnackbar('Successfully fetched the data.'))
.catch(() => this.props.enqueueSnackbar('Failed fetching data.'));
};
render(){
//...
};
};
export default withSnackbar(MyComponent);
I would like to place the enqueueSnackbar('my notification message') inside a class or some kind of helper function so that I can call the helper function from anywhere in the react app to display a message without having to wrap the export of a component with withSnackbar(MyComponent);. How can this be done?
I would achieve this via Context API like so:
create a context object which holds the enqueueSnackbar function
Then pass it from the uppermost App comp or any other parent comp
Access it anywhere inside any child component and invoke it as needed
Some pseduo code:
// context.js
import React from 'react';
import { useSnackbar } from 'notistack';
const { enqueueSnackbar } = useSnackbar();
const snackbarContext = React.createContext({ enqueueSnackbar });
export default snackbarContext;
Then wrap a parent component in the tree with this context's provider like so:
//parent.js
import SnackbarContext from './context.js'
const App = () => {
return (
<SnackbarContext.Provider>
<SomeParentComponent />
</SnackbarContext.Provider>
);
}
Now it can be used inside a dummy child component like so:
// child.js
import React, {useContext} from 'react'
import SnackbarContext from './context.js'
const DummyChild = ()=>{
const {enqueueSnackbar} = useContext(SnackbarContext);
return (
<div>
<h1>Dummy Component with snackbar invocation</h1>
<button onClick={() => enqueueSnackbar('Wohoooo')}>Show Snackbar</button>
</div>
)
}

How to replace a React component with a mock when testing with Jest

I am really stuck on this in the project I am working on, and all the answers I have found seem to be so simple but they haven't worked for me. Perhaps I don't really understand what a mock is, and I could really use some guidance.
I am testing a parent component that has a child component that fetches some data from a database using GraphQL. While testing the parent, I do not care what the child is doing. I want to replace the child with a mocked component (one that doesn't fetch data from a database) so that I can only focus on the parent.
I have come up with the simplest example possible to show my situation. Note that I'm using React Native and React Native Testing Library.
./src/ParentComponent.js
import React from 'react';
import { Text, View } from 'react-native';
import ChildComponent from './ChildComponent';
const ParentComponent = () => (
<View>
<Text>Hello World</Text>
<ChildComponent />
</View>
);
export default ParentComponent;
./src/ChildComponent.js
import React from 'react';
import { useQuery } from 'react-apollo';
import { View, Text } from 'react-native';
import gql from 'graphql-tag';
const CURRENT_USER_QUERY = gql`
query {
currentUser {
username
}
}
`;
const ChildComponent = () => {
const { data } = useQuery(CURRENT_USER_QUERY);
const { username } = data.currentUser;
return (
<View>
<Text>Welcome, {username}</Text>
</View>
);
};
export default ChildComponent;
./src/__mocks__/ChildComponent.js
import React from 'react';
import { Text } from 'react-native';
const ChildComponent = () => <Text>Welcome.</Text>;
export default ChildComponent;
./src/ParentComponent.test.js
import React from 'react';
import { MockedProvider } from '#apollo/react-testing';
import { render } from '#testing-library/react-native';
import ParentComponent from '../ParentComponent';
it(`should render the parent component.`, () => {
jest.mock('../ChildComponent');
const { getByText } = render(
<MockedProvider>
<ParentComponent />
</MockedProvider>
);
expect(getByText('Hello World')).toBeTruthy();
});
When I run the test, I get the following error...
● should render the parent component.
TypeError: Cannot read property 'currentUser' of undefined
14 | const ChildComponent = () => {
15 | const { data } = useQuery(CURRENT_USER_QUERY);
> 16 | const { username } = data.currentUser;
| ^
17 |
18 | return (
19 | <View>
at ChildComponent (src/ChildComponent.js:16:29)
It is still using the real <ChildComponent />. Why is it not replacing the <ChildComponent /> with the mocked version in the __mocks__ directory? Is that not how mocks work? If someone can please help and explain, it would be much appreciated. Thank you.
After days of wracking my brain on this, I finally figured out what was wrong. I was calling the mock inside of the it declaration. When I moved the line, jest.mock('../ChildComponent');, to the top, everything worked. So, the test file should look like this...
import React from 'react';
import { MockedProvider } from '#apollo/react-testing';
import { render } from '#testing-library/react-native';
import ParentComponent from '../ParentComponent';
jest.mock('../ChildComponent');
it(`should render the parent component.`, () => {
const { getByText } = render(
<MockedProvider>
<ParentComponent />
</MockedProvider>
);
expect(getByText('Hello World')).toBeTruthy();
});

useContext(Context) show warning on console in React Native

I have use React Context API hook in react native. I created Consumer and Provider both to pass props and state from parent to child. Code is working perfectly fine, I have received props from parent to child.
I have received following warning on my console:-
Warning: Calling useContext(Context.Consumer) is not supported, may
cause bugs, and will be removed in a future major release. Did you
mean to call useContext(Context) instead?
Please check below added code:-
I have create common context file CommonContext.js.
import React, {createContext} from 'react';
const MyContext = createContext();
export const LoginProvider = MyContext.Provider;
export const LoginConsumer = MyContext.Consumer;
This is my provider code
import React, {PureComponent} from 'react';
import {View, Text} from 'react-native';
import {LoginProvider} from './CommonContext';
export default class Login extends PureComponent {
constructor(props) {
super(props);
}
render() {
return (
<View style={Styles.mainContainer}>
<LoginProvider value={this.props}>
<LoginScreenView />
</LoginProvider>
</View>
);
}
}
From this way i am access parent component data into child hook
import {LoginConsumer} from './CommonContext';
export default function LoginScreenView(props) {
let loginConsumer = useContext(LoginConsumer);
return (
<View style={Styles.mainContainer}>
<Text>{loginConsumer}</Text>
</View>
);
}
You are receiving this error because you are calling useContext on a Context.Consumer instance, instead of a Context.
You do like this:
export const LoginConsumer = MyContext.Consumer;
and then you call useContext on LoginConsumer:
let loginConsumer = useContext(LoginConsumer);
Instead, you should call useContext on MyContext, like this:
Export the context from CommonContext.js:
export const MyContext = createContext();
Import the context instead of the context consumer in the file where you're using it:
import { MyContext } from './CommonContext';
and then using it with useContext:
let login = useContext(MyContext);
So your second component that you posted would look like this:
import { MyContext } from './CommonContext';
export default function LoginScreenView(props) {
let login = useContext(MyContext);
return (
<View style={Styles.mainContainer}>
<Text>{login}</Text>
</View>
);
}

Issues with initialising Redux in React while using mapDispatchToProps

I'm trying to learn some redux. Component I'm working on is a simble <div> based button that - when clicked - passes value as a parameter to the dispatch, so it can be displayed later someplace else.
After following both documentation and tutorials over the web I came up with the following code:
main app container
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import configureStore from './../store/configureStore.js'
import Input from './input.js'
let store = configureStore()
export default class App extends Component {
render() {
return (
<Provider store={store}>
<Input />
</Provider>
)
}
}
button container
import React, { Component } from 'react'
import { connect } from 'react-redux'
import printOut from './../actions/actions.js'
import InputComponent from './../components/inputComponent.js'
import PropTypes from 'prop-types'
const mapDispatchToProps = (dispatch) => {
return {
onClick: (input) => dispatch(printOut(input))
}
}
const Input = connect(mapDispatchToProps)(InputComponent)
export default Input
button component
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class Input extends Component {
render() {
return (
<div style={style} onClick={this.props.onClick('lala')}>Button!</div>
)
}
}
Input.PropTypes = {
onClick: PropTypes.func.isRequired
}
const style = {
height: 30,
width: 100,
backgroundColor: '#ff4068'
}
export default Input
Application breaks. I got this from the console:
Uncaught TypeError: (0 , _actions2.default) is not a function
at Object.onClick (index.js:33683)
at Input.render (index.js:33743)
(...)
index.js:22443 The above error occurred in the <Input> component:
in Input (created by Connect(Input))
in Connect(Input) (created by App)
in Provider (created by App)
in App
From what little I understood, there are some issues with button component and the way I'm trying to pass the param to props. So I tried to change it a little and added a function to handle that before render.
...
onClick(input) {
return this.props.onClick(input)
}
render() {
return (
<div style={style} onClick={onClick('lala')}>Button!</div>
)
}
...
The error I get this time is onClick is not defined. Oh, ok. I forgot this keyword before calling this new function. So I add it to the component and now I have
<div style={style} onClick={this.onClick('lala')}>Button!</div>
But the error being returned didn't really changed - it's again the original error of Uncaught TypeError: (0 , _actions2.default) is not a function
I'm starting to run out of ideas now. Could someone please tell me how what my be the issue here?
Help me Stack Overflow, you're my only hope! to quote timeless classic.
Are you sure you are importing printOut in properly? Shouldn't it be import { printOut } from './../actions/actions.js' ?
Then, first argument in connect is mapStateToProps and the second is mapDispatchToProps this is probably why you have dispatch is not a function.
You are importing InputComponent:
import InputComponent from './../components/inputComponent.js'
but inside button component you are exporting it as Input:
export default Input
so change InputComponent with :
import Input from './../components/inputComponent.js'
Use this for connect
export default connect(mapDispatchToProps)(Input)
You are facing 2 problems.
1. Syntax problem in your import
The following problem Uncaught TypeError: (0 , _actions2.default) is not a function is caused by the import of your actions.
Instead of
import printOut from './../actions/actions.js'
It should be
import { printOut } from './../actions/actions.js'
2. You are incorrectly using connect
connect accepts these two arguments with the following order:
mapStateToProps: contains the props you want to give to the component
mapDispatchToProps: contains the actions to dispatch
Even if you could call your action, there is no way the dispatch will happen because you call the reducers instead of the dispatch.
What you should do is the following:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { printOut } from './../actions/actions.js';
import InputComponent from './../components/inputComponent.js';
import PropTypes from 'prop-types';
const mapDispatchToProps = (dispatch) => {
return {
onClick: (input) => dispatch(printOut(input))
}
}
export default connect(null, mapDispatchToProps)(InputComponent);
You can read this documentation for more details: http://redux.js.org/docs/basics/UsageWithReact.html#implementing-container-components

Resources