I am a freshman in react ,I want to write a react component of getting the member info of a team by teamId.
React code
import React from 'react';
import PropTypes from 'prop-types';
import UserTable from './pm_user_table';
import {Form,Modal,Input,Button} from 'antd';
const FormItem = Form.Item;
class PMBody extends React.Component{
constructor(props){
super(props);
this.state={
curTeam:this.props.curTeam,
memberList:[]
}
}
componentWillMount(){
console.log('component mount');
}
componentWillReceiveProps(nextProps){
if(nextProps.curTeam !== this.state.curTeam){
this.setState({curTeam:nextProps.curTeam});
}
}
render(){
let {getFieldProps} = this.props.form;
const teamId = this.state.curTeam;
var myFetchOptions={method: 'GET'};
fetch("http://localhost:3001/teamMembers/" +this.state.curTeam,myFetchOptions)
.then(response=>response.json())
.then(json => {
this.setState({memberList:json});
}
).catch(function(){
console.log("error");
});
let memberList = this.state.memberList;
const body = memberList !='' ?
<UserTable dataSource={memberList} actions={this.props.actions} />
:
''
;
return (
<div>
{body}
</div>
)
}
PMBody.PropTypes = {
curTeam:PropTypes.string.isRequired,
actions: PropTypes.object.isRequired
}
export default PMBody =Form.create({})(PMBody);
By the network view in chrome devtool,It seems that the browser request the same url repeatedly.
So why it fetch the same url repeately?
You're misunderstanding the purpose of the render() method.
React calls render() to update your component anytime anything changes. It must be pure and should not interact with anything else.
You should move that to componentDidMount().
Related
I have a web app that is suppose to show a list of notes made by the user on the dashboard if said list exist (that is if the user wrote any note at all). I wrote the reducer, the actions and I connected state and dispatch in order for it to work. But for some reason the notes created don't appear once in the dashboard when I write them, I already made sure that the ADD_NOTE action gets fired and that the reducer updates the data in redux, but in the dashboard component that data disappears.
This is my reducer.
export default (state = [], action) => {
switch (action.type) {
case "ADD_NOTE":
return [
...state,
action.note
];
case "REMOVE_NOTE":
return state.filter(({ id }) => id !== action.id);
default:
return state;
}
}
And those are my actions
import { v4 as uuidv4 } from 'uuid';
export const addNote = ({ title = "", body = ""} = {}) => ({
type: "ADD_NOTE",
note : {
title,
body,
id : uuidv4()
}
});
export const removeNote = ({ id } = {}) => ({
type: "REMOVE_NOTE",
id
});
This is the component that holds the create note form.
import React, { Component } from 'react';
class CreateNote extends React.Component{
constructor(props){
super(props);
this.onTitleChange = this.onTitleChange.bind(this);
this.onBodyChange = this.onBodyChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
title: "",
body: "",
error: ""
}
}
onTitleChange(e){
const title = e.target.value;
this.setState({ title });
}
onBodyChange(e){
const body = e.target.value;
this.setState({ body });
}
onSubmit(e){
e.preventDefault();
if(!this.state.title || !this.state.body){
this.setState({ error : "Please fill in all gaps"});
} else {
this.setState({ error: ""});
const data = { title: this.state.title, body: this.state.body}
this.props.onChange(data);
}
}
render(){
return(
<div>
{this.state.error && <p>{this.state.error}</p>}
<form onSubmit = {this.onSubmit}>
<label>Put a title for your note</label>
<input
placeholder="Title"
type="text"
value={this.state.title}
autoFocus
onChange = {this.onTitleChange}
/>
<label>Write your note</label>
<textarea
placeholder="Note"
value={this.state.body}
autoFocus
onChange = {this.onBodyChange}
/>
<input type="submit" value="Submit"/>
</form>
</div>
);
}
}
export default CreateNote;
And this is the component that fires the ADD_NOTE action
import React, { Component } from 'react';
import CreateNote from "./actions/CreateNote";
import Header from "./Header";
import { addNote } from "../actions/noteActions"
import { connect } from 'react-redux';
class Create extends React.Component{
constructor(props){
super(props);
this.eventHandler = this.eventHandler.bind(this);
}
eventHandler(data){
this.props.addNote(data);
this.props.history.push("/");
}
render(){
return (
<div>
<Header />
<CreateNote onChange = {this.eventHandler}/>
</div>
)
}
}
const mapDispatchToProps = (dispatch) => ({
addNote: (note) => dispatch(addNote(note))
});
export default connect(null, mapDispatchToProps)(Create);
And finally this is the dashboard component that renders the notes if they exist
import React from "react";
import ListItem from "./actions/ListItem";
import { connect } from 'react-redux';
const ListGroup = (props) => (
<div>
{
props.notes.length === 0 ? <h1>Write a note!</h1> :
(
props.notes.map((note) => {
return <ListItem key={note.id} {...note} />;
})
)
}
</div>
)
// The mapStateToProps does not connect with the local state, the action ADD_NOTE fires whenever
// the Create form is submited and the reducer updates the redux storage. So the problem lies here ?
// It could be that state.note is not definded but I don't know where should I define it if I have to,
// and apparently I don't have to ???????????????
const mapStateToProps = (state) => {
return {
notes: state.note
};
};
export default connect(mapStateToProps)(ListGroup);
When I try to run this it fires an error:
ListGroup.js?11a1:5 Uncaught TypeError: Cannot read property 'length' of undefined
at ListGroup (ListGroup.js?11a1:5)
Showing that the data that gets passed to the props is undefined. I'm thinking that it could be that state.note is not defined and I have to define it somewhere but I don't know if that's the case.
Use Hooks in functional components
connect() is only valid for class based components. For functional components you need to use hooks. Specifically the useSelector hook for reading redux state and useReducer to emit actions. You can find more instructions on redux hooks here https://react-redux.js.org/api/hooks#useselector
How do I create a component for Gatsby that will load on the client-side, not at build time?
I created this one and it renders with gatsby develop but not with the rendered server-side rendering
import React from 'react';
import axios from 'axios';
import adapter from 'axios-jsonp';
export default class Reputation extends React.Component<{}, { reputation?: number }> {
constructor(props) {
super(props);
this.state = {};
}
async componentDidMount() {
const response = await axios({
url: 'https://api.stackexchange.com/2.2/users/23528?&site=stackoverflow',
adapter
});
if (response.status === 200) {
const userDetails = response.data.items[0];
const reputation = userDetails.reputation;
this.setState({
reputation
});
}
}
render() {
return <span>{ this.state.reputation?.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") }</span>
}
}
If you don't want the component to be bundled in the main js file at build time, use loadable-components
Install loadable-components and use it as a wrapper for a component that wants to use a client-side only package. docs
import React, { Component } from "react";
import Loadable from "#loadable/component";
const LoadableReputation = Loadable(() =>
import("../components/Reputation")
);
const Parent = () => {
return (
<div>
<LoadableReputation />
</div>
);
};
export default Parent;
before render this component, make sure you have a window, to detect if there is a window object. I would write a hook for that:
function hasWindow() {
const [isWindow, setIsWindow] = React.useState(false);
React.useEffect(() => {
setIsWindow(true);
return ()=> setIsWindow(false);
}, []);
return isWindow;
}
In the parent component check if there is a window object:
function Parent(){
const isWindow = hasWindow();
if(isWindow){
return <Reputation />;
}
return null;
}
I'm want to render data from firestore into my react component. I updated the global state array with firestore data and it's updating but when I'm going to render that array the array shows as undefined.
I have tried using redux and the same problem happened, now used reactn but same things are happening.
import React from "react";
import {setGlobal} from "reactn";
import ReactDOM from "react-dom";
import Apps from "./Apps";
setGlobal({ names:[],})
ReactDOM.render( <Apps/>, document.getElementById("root"))
ReactDOM.render(<Apps/>, document.getElementById("root"))`
-----App.js----------
import React from "reactn";
import db from "./firebase";
class Apps extends React.Component{
componentDidMount(){
db.collection("users").get().then((snapshot)=>{
snapshot.forEach((doc)=>{
const user= {name:doc.data().name,
weight:doc.data().weight,
id:doc.id}
this.global.names.push(user)
})
})
}
render(){
///this show the data in names array of state
console.log(this.global)
//// this show undefind (its having data)
console.log(this.global.names[0])
return(
///but while rendering its not showing anything
<div>{this.global.names.map((name)=>(
<h1>weight is {name.weight} </h1>
)
)}</div>
)
}
}
export default Apps;
instead of
this.global.names.push(user)
You have to use
this.setGlobal(names: names.push(user))
I think don't use global variable in react just do something like that
class Apps extends React.Component {
constructor(props) {
super(props)
this.state = {
names: [],
}
}
componentDidMount() {
let array = [];
db.collection("users").get()
.then((snapshot) => {
snapshot.forEach((doc) => {
const user = {
name: doc.data().name,
weight: doc.data().weight,
id: doc.id
}
array.push(user)
})
})
.then(() => {
this.setState({
names : array
})
})
}
render() {
///this show the data in names array of state
console.log(this.state.names)
//// this show undefind (its having data)
console.log(this.state.names[0])
return (
///but while rendering its not showing anything
<div>{this.state.names.map((name) => (
<h1>weight is {name.weight} </h1>
)
)}</div>
)
}
}
export default Apps;
Try this and tell me if it's works :)
I'm currently using Flickr api to make a Simple Image Carousel and facing a problem where my state does not get updated or rendered whenever I click the button.
Here is my index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import _ from 'lodash';
import Photo from './components/photo';
const urlArr = [];
const apiKey = "API";
const userId = "ID";
const url = `https://api.flickr.com/services/rest/?method=flickr.people.getPublicPhotos&api_key=${apiKey}&user_id=${userId}&format=json&nojsoncallback=1`;
class App extends Component {
constructor(props) {
super(props);
this.state = { urlArr: [] };
axios.get(url)
.then((photoData) => {
_.forEach(photoData.data.photos.photo, (photo) => {
urlArr.push(`https://farm6.staticflickr.com//${photo.server}//${photo.id}_${photo.secret}_z.jpg`);
});
this.setState({ urlArr });
});
}
render() {
return (
<div>
<Photo urls={this.state.urlArr}/>
</div>
);
}
};
ReactDOM.render(<App/>, document.querySelector('.container'));
and here is my photo.js
import React, { Component } from 'react';
import NextButton from './nextButton';
import PrevButton from './prevButton';
class Photo extends Component {
constructor(props) {
super(props);
this.state = { idx: 0 };
this.nextImg = this.nextImg.bind(this);
}
nextImg() {
this.setState({ idx: this.state.idx++ });
}
render() {
if (this.props.urls.length === 0) {
return <div>Image Loading...</div>
}
console.log(this.state);
return(
<div>
<PrevButton />
<img src={this.props.urls[this.state.idx]}/>
<NextButton onClick={this.nextImg}/>
</div>
);
}
}
export default Photo;
and my nextButton.js (same as prevButton.js)
import React from 'react';
const NextButton = () =>{
return (
<div>
<button>next</button>
</div>
);
};
export default NextButton;
Since I'm fairly new to React, I'm not quite sure why my this.state.idx is not getting updated when I click on the next button (Seems to me that it is not even firing nextImg function either). If anyone can give me a help or advice, that would really appreciated.
Thanks in advance!!
Update your NextButton. Use the event within your presentational component.
<NextButton next={this.nextImg}/>
And the NextButton component should looks like this.
import React from 'react';
const NextButton = (props) =>{
return (<div>
<button onClick={props.next}>next</button>
</div>
);
};
The problem lies with this piece of code:
axios.get(url)
.then((photoData) => {
_.forEach(photoData.data.photos.photo, (photo) => {
urlArr.push(`https://farm6.staticflickr.com//${photo.server}//${photo.id}_${photo.secret}_z.jpg`);
});
this.setState({ urlArr });
});
this refers to the axios.get callback scope and not the Component. You can define another variable called self or something that makes more sense to you and call self.setState().
See this question for a similar problem: Javascript "this" scope
I'm new to redux and having trouble wrapping my head around presentational and container components.
Relevant stack:
react v0.14.8
react-native v0.24.1
redux v3.5.2
react-redux v4.4.5
The issue:
I have a login button component, which when rendered checks the login status and calls the onSuccessfulLogin action which updates the state with the user's Facebook credentials.
However, when trying to separate this into separate presentational/container components, I'm unable to call the onSuccessfulLogin action: Error: onSuccessfulLogin is not defined.
What am I doing wrong here? I'd imagine there's something simple that I'm not understanding with the relationship between the two components and the connect() function.
Presentational Component (Login.js)
import React, { PropTypes } from "react-native";
import FBLogin from "react-native-facebook-login";
import UserActions from "../users/UserActions";
class LoginPage extends React.Component {
render() {
const { userData, onSuccessfulLogin } = this.props;
return (
<FBLogin
permissions={["email","user_friends"]}
onLoginFound= { data => {
onSuccessfulLogin(data.credentials);
}}
/>
)
}
};
export default LoginPage;
Container Component (LoginContainer.js)
import { connect } from 'react-redux';
import LoginPage from "../login/LoginPage";
import UserActions from "../users/UserActions";
const mapDispatchToProps = (dispatch) => {
return {
onSuccessfulLogin: (userData) => {
dispatch(UserActions.userLoggedIn(userData))
}
}
}
const mapStateToProps = (state) => {
return {
userData: state.userData
}
}
const LoginContainer = connect(
mapStateToProps,
mapDispatchToProps
)(LoginPage);
export default LoginContainer;
Also, if I wanted to make the updated state.userData accessible to the LoginPage component, how would I do that? Any help is appreciated!
Solved! When using ES6 classes, you're required to call super(props) in a constructor method in order to access the container's properties in the connected presentational component:
class LoginPage extends React.Component {
constructor(props){
super(props);
}
render(){
// ...
}
}
Your container component is supposed to be a component and it must have a render function with the dumb/presentational components you want to render.
import { connect } from 'react-redux';
import LoginPage from "../login/LoginPage";
import UserActions from "../users/UserActions";
class LoginContainer extends React.Component {
constructor(props){
super(props);
}
render() {
return (
<LoginPage userData={this.props.userData}
onSuccessfulLogin={this.props.onSuccessfulLogin}
/>
)
}
};
const mapDispatchToProps = (dispatch) => {
return {
onSuccessfulLogin: (userData) => {
dispatch(UserActions.userLoggedIn(userData))
}
}
}
const mapStateToProps = (state) => {
return {
userData: state.userData
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(LoginPage);