I'm trying to get some additional data from components and I don't know how. If it's possible, how can I get that and set it to a state?
It sounds like you're looking for props. The example from that documentation page is the Image component, which takes a source prop:
import React, { Component } from 'react';
import { AppRegistry, Image } from 'react-native';
export default class Bananas extends Component {
render() {
let pic = {
uri: 'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'
};
return (
<Image source={pic} style={{width: 193, height: 110}}/>
);
}
}
// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => Bananas);
Your component receives props as the first argument to its constructor (if it's a class) or function (if it's a stateless functional component).
If it's possible, how can I get that and set it to a state?
You wouldn't usually do that. You might set state based on props (for instance: if you had a counter, you might have a prop for what number to start with and a state item for the current number), but usually you don't copy props to state directly. You can use props when rendering.
Related
I just learned that we can reduce the complexity of a react project using redux. With the single source of truth (store), we don't need to pass down states to components that don't need them. I'm struggling with understanding this statement.
Say I have three components, A, B and C. A is a container with a state called text. B is a custom button and C only displays the text. Whenever B is clicked, it updates the state in A. Then C will display the updated text.
A
/ \
C B
I have tried to apply redux to the app and found that I still need to pass down the props. The only difference is that I am passing down this.props.text instead of this.state.text.
I can't see how redux can benefit an app like this.
App.js
import React, { Component } from "react";
import { connect } from 'react-redux';
import MyButton from "./MyButton";
import { handleClick } from "./actions";
import Display from "./Display"
class App extends Component {
render() {
return (
<div className="App">
<MyButton onClick={()=>this.props.handleClick(this.props.text)} />
<Display text={this.props.text} />
</div>
);
}
}
const mapStateToProps = state => ({
text: state.text.text
})
const mapDispatchToProps = dispatch => ({
handleClick: (text) => dispatch(handleClick(text))
})
export default connect(mapStateToProps, mapDispatchToProps)(App)
Also, if we have another app with structure shown below. Say B doesn't care about A's state but C needs it to display the text. Can we skip B and just let C use A's state?
A
|
B
|
C
I think I found the solution. I simply created a file stores.js and
export the store. So I can import it and retrieve the state by
invoking store.getState() whenever a child component needs the it.
You shouldn't do that.
Instead you should use the connect function with each component, everywhere in the structure, that needs access to a property of your store.
But, if you only have three components, you probably don't need Redux or a global store for your app state.
Redux comes with a lot of opinions on how to handle your global state that are meant to secure your data flow.
Otherwise, if you only need to avoid prop drilling (i.e. passing down props through many levels, as in your second exemple) you may use the native React context API that does just that: reactjs.org/docs/context.html
Edit
Things should be clearer with an exemple:
import React, { Component } from "react";
import { connect } from 'react-redux';
import MyButtonCmp from "./MyButton";
import DisplayCmp from "./Display"
import { handleClick } from "./actions";
// I am doing the connect calls here, but tehy should be done in each component file
const mapStateToProps = state => ({
text: state.text.text
})
const Display = connect(mapStateToProps)(DisplayCmp)
const mapDispatchToProps = dispatch => ({
onClick: (text) => dispatch(handleClick(text))
})
const MyButton = connect(null, mapDispatchToProps)(MyButtonCmp)
class App extends Component {
render() {
return (
<div className="App">
{/* No need to pass props here anymore */}
<MyButton />
<Display />
</div>
);
}
}
// No need to connect App anymore
// export default connect(mapStateToProps, mapDispatchToProps)(App)
export default App
In this example, you may map app state to props using redux.
I don't see why you would process the information this way(with redux) unless you were planning on using the data in multiple parts of the application and wanted to re-use the action code.
See more:
https://react-redux.js.org/using-react-redux/connect-mapstate
2nd question
Also, if we have another app with structure shown below. Say B doesn't care about A's state but C needs it to display the text. Can we skip B and just let C use A's state?
In Redux, yes.
With React Hooks, yes.
I have this component:
import React from "react";
import Snackbar from "#material-ui/core/Snackbar";
const GlobalMessage = function(props) {
return (
<Snackbar
anchorOrigin={{
vertical: "top",
horizontal: "center"
}}
open="{props.error}"
autoHideDuration={3000}
ContentProps={{
"aria-describedby": "message-id"
}}
message={<span id="message-id">{props.error}</span>}
/>
);
};
export default GlobalMessage;
To send string to that function component from another component I have these codes but, none of them are working:
GlobalMessage({error:"this.props.error"})
GlobalMessage.bind(null,{error:"this.props.error"})
GlobalMessage("This is an error!")
How to send string or data to component function ? And, I don't want to use it like this:
<GlobalMessage error={this.props.error} />
In React, the core of reactivity is the state and the props. Since you want to use a functional component you are down to props.
In my understanding, you want to send this functional component "strings" from other different components, so I guess you want to use a single component and rerender it whenever you pass it a new string. There is no simple solution to achieve this. If you want to use a single GlobalMessage component, using GlobalMessage("some string") is not the way to go, since it will create a new one on each call.
The simplest solution to achieve this that comes to my mind, without using data stores and libraries that assure reactivity like Redux or Mobx, is to somewhat adopt presentational and container components (read more here).
You can create a top-level stateful class component to hold your string, lets call it AppManager, then display any components that you want from there.
An example of such a component:
class AppManager extends React.Component {
state = {
message: ""
}
setMessage = (message) => {
this.setState({message});
}
render() {
return (
<React.Fragment>
<App
setMessage={this.setMessage}
>
<GlobalMessage error={this.state.message} >
</React.Fragment>
)
}
}
You can then pass the setMessage function along in the props to other components.
In the App component you can call this.props.setMessage(message) to send the message to GlobalMessage.
A more elegant and scalable solution would involve using libraries like cartiv, redux or mobx, but I advise you to look them up.
Hope it helps!
Probably a simple question with a simple answer, but I can't figure this out. Blame the heat. My simulator prints 'this is a test' on the screen, when I want it to say 'changed'. What am I doing wrong?
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Template from './src/components/Template';
export default class App extends React.Component {
constructor(props) {
super(props);
this.foo= "this is a test";
}
changeMe = () => {
this.foo = 'changed';
}
componentDidMount(){
this.changeMe();
}
render() {
return (
<Template foo={this.foo} />
);
}
}
Use state
You are using a class attribute witch will not trigger a re-render when changed. In react a component will re-render when it receives new props or when the state is changed (there's also a way to force it but best not to do that).
Example using the state to trigger a rerender
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Template from './src/components/Template';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state ={foo: "this is a test"};
}
changeMe = () => {
this.setState({foo:'changed'})
}
componentDidMount(){
this.changeMe();
}
render() {
return (
<Template foo={this.state.foo} />
);
}
}
When passing new props to a component it will also re-render (unless you implement componentShouldUpdate).
State explanation
So inside a react component you have a local state object in this.state it can be set in the constructor like this.state = {bar: 'foo'};. After the constructor has set the state it should only be changed with the this.setState() method.
Upon changing the state with setState the component will re-render with the updated values.
The state is not available outside of the component, at least not available as this.state because that would call the local state of the current component.
If you need to use a value from the state of a parent component you can pass it to a child. At that point it becomes a prop of the child accessible with
this.props
To change the value of state from a child component you should pass a function to the child that changes the state of the parent.
This process of passing state changing functions becomes increasingly complex as your app grows, I would suggest using a library like Redux to manage app state via actions and reducers. There is a steep learning curve but once you have a grasp of this modified flux methodology you will wonder how you ever lived without it.
I was trying to learn meteor and not very familiar with this pattern of HOC (it's meteor.js with react).
I was going though their offical docs of tutorials. Here is what they did (You can click here to visit the page)
They imported following package in App.js
import React, { Component } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import { Tasks } from '../api/tasks.js';
Then there is a simple to do class App extends component wrapped by this HOC
export default withTracker(() => {
return {
tasks: Tasks.find({}).fetch(),
};
})(App);
The official docs for the same says
The wrapped App component fetches tasks from the Tasks collection and
supplies them to the underlying App component it wraps as the tasks
prop. It does this in a reactive way, so that when the contents of the
database change, the App re-renders, as we'll soon see!
Now this language isn't exactly alien to me but I am having hard to comprehend and understand it. So Can someone explain me the same?
To be specific what is The wrapped App component fetches tasks and supplies it to underline app component
A higher order component is in the most basic form a function that takes a component type as input and returns a component class that wraps the input component and adds functionality to it.
Usually the signature is function that takes the argument to apply to the wrapped component which returns a HOC as described above so you can use it with multiple components.
Here is a very basic example that shows an error message if the component it's used on or any of it's child components throw an exception:
const catchError = ({ errorMessage }) => InputComponent => class extends Component {
render() {
try {
return <InputComponent {...this.props} />;
} catch {
return <div>{errorMessage}</div>
}
}
}
const ComponentWithErrorMessage = catchError({ errorMessage: 'Whoops!' })(MyComponent);
// This is the same as the following, the first just immediately invokes the returned function
const whoopsErrorHoc = catchError({ errorMessage: 'Whoops!' });
const ComponentWithWhoopsError = whoopsErrorHoc(MyComponent);
The meteor HOC will be a bit more complicated but the idea is the same. It receives a reference to the meteor Task store and will return a component that re-renders the input component whenever the data changes in the store and add the data to the props of that component.
I have a react component that receives some props. These props need to be mapped to a different structure in order to be usable (for a select, which expects a different structure than our store). I'm wondering at which point in the lifecycle I should do this.
I've read the documentation: https://facebook.github.io/react/docs/component-specs.html, and it recommends to keep the render function pure:
The render() function should be pure, meaning that it does not modify
component state, it returns the same result each time it's invoked,
and it does not read from or write to the DOM or otherwise interact
with the browser
Now I'm assuming that it's still ok to map the props, as that's not state and I'm not changing them:
import React from 'react';
export default class IndicatorSelect extends React.Component {
constructor(props) {
super(props);
}
render() {
const options = this.props.store.items.map(item => ({ value: item.id, label: item.name }));
return (
<div>Display the data in some way here: {options.value} {options.label}</div>
);
}
}
ThisComponent.propTypes = {
store: React.PropTypes.object.isRequired,
};
Is this the recommended way, or should the parent component be responsible for formatting the props passed to this component? Or I should I do this in a lifecycle method, or a completely different manner?
Yes, #selvagsz comment is right - normalising props inside render is not that bad unless you do it heavily. If it's "too much", I would create a container component that normalises the props and passes them to a presentational one that only takes care of rendering.
It is not easy to answer the question, but a rule of thumb I follow is:
A presentational component should receive its props in the most convenient / normalised way possible so that it only takes care of rendering.
However, for normalisation as in your example it doesn't make sense to create additional container component - the overhead is too big at this point. When you start feeling the pain of converting props, create a container component and let it convert them for you or do it in the parent if it's already a container.