Can't render image from state in React/JSX - image

I'm getting the path to an image from my database. The image file is stored locally. I store the path as State for the component thus keeping the component dynamic as opposed to simply importing the path form it's location. So...
this works...
render() {
return (
<div>
<Col xs={6} md={4}>
<Image src={require('../../../../public/uploads/file-1516414373384.png')}responsive />
</Col>
</div>
)
However, this does not...
class Reports extends Component {
constructor(props) {
super(props);
this.state = {
reports: [],
photos: null
}
}
componentWillMount() {
var reports = []
axios.get('/uploadUserImage')
.then( (response) => {
response.data.forEach(function(report){
reports.push(report)
})
}).then(() => {
this.setState({reports})
}).then(() => {
var path = '../../../../'+this.state.reports[0].file;
var rightPath = path.replace(/\\/g,"/");
this.setState({photos: rightPath})
})
.catch( (error) => {
console.log(error);
});
}
render() {
return (
<div>
<Col xs={6} md={4}>
<Image src={require(this.state.photos)}responsive />
</Col>
</div>
)
Even though the second non-working code compiles to the same this as the first working bit of code.
I get an error that says
Uncaught Error: Cannot find module "."
So maybe there is something wrong in my web-pack? but if that were true wouldn't both cases throw the error???
I also tried template literals...
<Image src={require(`${this.state.photos}`)}responsive />
As was the answer to a similar question, but no dice - Same error.

I think, this is because first time it trying to import image of path null (initial value of the path in state), only after getting successful response from server, it will have the correct image path.
One possible solution is, render the image only when you have a valid path means after getting response, Use conditional rendering and put the check.
Like this:
{this.state.photos ? <Image src={require(this.state.photos)} responsive /> : null}

It’s OP. I tried everything suggested here and in other similar questions. Nothing worked. I installed react-image and now everything works fine.
Wish I could give an explanation as to what’s going on under the hood, and what exactly went wrong before, but I don’t really know. Images do render now via component state. So, victory I guess.

I was running into a similar issue and found this to work in my project:
import React, { Component } from 'react';
class ImageUpload extends Component {
constructor(props) {
super(props);
this.state = {
file: null
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = e => {
this.setState({
file: URL.createObjectURL(e.target.files[0])
});
};
render() {
const fileAttached = this.state.file;
return (
<div>
<input type="file" onChange={this.handleChange} />
{fileAttached ? (
<img
src={this.state.file}
alt="File Uploaded"
height="300"
width="400"
/>
) : (
<img src="" alt="No file uploaded" />
)}
</div>
);
}
}
export default ImageUpload;

Related

How to show error on button click in React native?

I am using 'react-native-material-textfield' and it working well, but I need to show error for empty field when clicking on submit button. I have searched lot-of but didn't find any solution.
Put the error message in your state and fill it with a message after clicking on the submit button, if your validation process fails.
render(){
return (
<View>
<TextField
{...props}
error={this.state.error}
errorColor={'red'}
onFocus={() => this.setState({error: ''})}
/>
<Button {...props} />
</View>)}
Check the example on the developers github repository.
According to the module documentation and examples, whenever your this.state.errors for each field is not empty, its error is shown. So your form should look like this:
class Form extends Component {
// ... Some required methods
onSubmit() {
let errors = {};
['firstname'] // This array should be filled with your fields names.
.forEach((name) => {
let value = this[name].value();
if (!value) {
errors[name] = 'Should not be empty'; // The error message when field is empty
}
});
this.setState({ errors });
}
render() {
let { errors = {}, data } = this.state;
return (
<View>
<TextField
value={data.firstname}
onChangeText={this.onChangeText}
error={errors.firstname}
/>
<Text onPress={this.onSubmit}>Submit</Text>
</View>
);
}
}

In list component, how to implement a component to change the number of results displayed

I was thinking about making a simple component with a Select and the list of results that should be displayed.
After reading the code, that seems impossible, because if I change the url, then update is triggered by componentWillReceiveProps, and this method does not check for a change of perPage
Change the prop perPage of the List component does not work either because the List use this prop only if the query does not already contains perPage
Here is an example of what I want to do :
import { List } from "admin-on-rest";
class SourceList extends Component {
constructor(props) {
super(props);
this.state = {
perPage: 10
};
}
render() {
return (
<div>
<Button
onClick={() => {
this.setState({ perPage: 50 });
}}
/>
<List {...props} perPage={this.state.perPage}>
... Here would be the content of the list
</List>
</div>
);
}
}

Infinite loop when dispatching action that performs an AJAX request - ReactJS, Redux, Redux-saga

I am trying to load data for a component using redux-saga and axios but I keep getting an infinite loop. These are the relevant components:
App.js
class App extends React.Component {
render() {
return (
<div className="app">
<header>
<div className="pure-u-1 pure-u-sm-1-3">
</div>
<nav className="pure-u-1 pure-u-sm-2-3">
<ul>
<li><Link to="/meal-plans">Meal Plans</Link></li>
</ul>
</nav>
</header>
<main>
<div className="view-area">
<Switch>
<Route exact path="/" component={() => <DashBoard {...this.props} />} />
<Route exact path="/meal-plans" component={() => <MealPlansContainer {...this.props} />} />
<Route path="/meal-plans/:id" component={props => <MealPlan {...this.props} {...props} />} />
</Switch>
</div>
</main>
</div>
)
}
}
function mapStateToProps(state) {
return {
app: state.app,
mealPlan: state.mealPlan,
mealPlans: state.mealPlans,
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
}
App = withRouter(connect(mapStateToProps, mapDispatchToProps)(App));
export default App;
MealPlan.js
class MealPlan extends React.Component {
componentDidMount() {
let id = this.props.match.params.id
this.props.fetchMealPlan(id);
}
render() {
return (
<div className="meal-plan pure-g box">
<div className="pure-u-1">
<p>Future Meal Plan Page</p>
<LoadingSpinner show={true} />
</div>
</div>
);
}
}
I'm new to React and I don't have a good grasp on the component lifecycle, but I believe that componentDidMount() is being called each time the AJAX request completes and the 'FETCH_MEAL_PLAN_SUCCESSFUL' case runs in the reducer.
actionCreators.js
export function fetchMealPlan(id) {
return {
type: 'FETCH_MEAL_PLAN',
id
}
}
export function fetchMealPlanSuccessful(data) {
return {
type: 'FETCH_MEAL_PLAN_SUCCESSFUL',
data
}
}
export function fetchMealPlanFail(message) {
return {
type: 'FETCH_MEAL_PLAN_FAIL',
message
}
}
sagas.js
function* fetchMealPlan(action) {
try {
const mealPlan = yield call(api.get, '/meal_plans/' + action.id);
yield put(fetchMealPlanSuccessful(mealPlan.data));
} catch (e) {
yield put(fetchMealPlanFail(e.message));
}
}
function* watchFetchMealPlan() {
yield takeLatest('FETCH_MEAL_PLAN', fetchMealPlan);
}
mealPlan.js (reducer)
function mealPlan(state = {}, action) {
switch(action.type) {
case 'FETCH_MEAL_PLAN':
return state;
case 'FETCH_MEAL_PLAN_FAIL':
return state;
case 'FETCH_MEAL_PLAN_SUCCESSFUL':
state = Object.assign({}, action.data);
return state;
break;
default:
return state;
}
}
export default mealPlan;
If I don't stop the app, it'll keep making requests. In this test, it made 4,200+ requests:
App in an infinite loop
I've spent hours researching how to best load data from my API for components after changing a route and so far I've been unsuccessful. Thank you in advance for your help.
Normally a component should run componentDidMount once, unless it is deallocated and then mounted again. If its props or state change, then componentDidUpdate will be fired.
So if you are experiencing multiple componentDidMount, then it is likely that this component is nested as a child (I guess you did from your code snippet), and then when its parent's (App) state or props change, which will re-render its children (including MealPlan). Because you are connecting mealPlan & mealPlan from redux to App. Whenever you call the API, App's props will be updated, thus causing its children to be re-rendered, and causing MealPlan trigger componentDidMount again and again.
I am not super experience with React. But I have not seen the way you set up routes. One way is you define a router in separate file, and use the router like this (this is not using the code-splitting feature of react-router):
const router = (
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="/meal-plans" component={MealPlan} />
</Route>
</Router>
)
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}> // react-redux provider
{ router }
</Provider>,
document.querySelector('#main'));
If you wanna do code-splitting, since you are using redux-sagas, a good template is https://github.com/react-boilerplate/react-boilerplate. By looking at the ways how they do it, you could achieve what you want

how to combine checkbox with text input in reactjs

i am trying to build a Ui component in Reactjs which combines a checkbox and a text input attched to it (instead of a text label) so that if the checkbox is checked , the user can change the text input , and if its unchecked the user will not be able to do so
the final goal is to render outside of the component all of textinputs valus which left checked as a list or as a menu item.
Its should look like this :
Checkbox with Text input
anyone knows how should i do this ? im new to reactjs and got a bit confused how to pass logic between two components(as in here between the checkbox and the text input and between the "combo" component and the outer rendered list) .
thanks in advance !
EDIT1:
well i managed to build the component but i cant make the children call the parent handler (handlerCheckbox , handlerInput)in order to actually make the magic happen.
anything im doing wrong ?
this is the child:
class CheckboxTxtInput extends React.Component{
constructor(props){
super(props);
console.log(props.isChecked)
}
handleCheckboxChild(e) {
this.props.handleCheckbox(e,this.props.id)
}
handleInputChild(e){
this.props.handleInput(e,this.props.id)
}
render(){
return (
<div>
<input type="checkbox" onChange={this.handleCheckboxChild} defaultChecked={this.props.isChecked} />
<input type="text" value={this.props.inputValue} disabled={!this.props.isChecked} onChange={this.handleInputChild}/>
</div>
)
}
}
This is the parent:
export default class Text extends React.Component {
constructor(props) {
super(props);
this.state = {
textItems: [{id:0,inputValue:'text',isChecked:true},{id:1,inputValue:'text',isChecked:true}
,{id:2,inputValue:'text',isChecked:true},{id:3,inputValue:'text',isChecked:true}]
};
this.handleCheckbox = this.handleCheckbox.bind(this);
this.handleInput= this.handleInput.bind(this);
}
handleCheckbox(e,id) {
var stateCopy = Object.assign({}, this.state);
stateCopy.textItems[id].isChecked = e.target.value;
this.setState(stateCopy);
}
handleInput(e,id){
var stateCopy = Object.assign({}, this.state);
stateCopy.textItems[id].text = e.target.value;
this.setState(stateCopy);
}
render () {
return (
<div>
<hr className="divider-long"/>
<UI.sectionDividerLabeled label="Show/Hide Text"/>
<hr className="divider-long"/>
<p>Here you can show\hide your text</p>
<div>
<CheckboxTxtInput id={this.state.textItems[0].id} isChecked={this.state.textItems[0].isChecked}
inputValue={this.state.textItems[0].inputValue} handleInput={this.handleInput}
handleCheckbox={this.handleCheckbox} />
<CheckboxTxtInput id={this.state.textItems[1].id} isChecked={this.state.textItems[1].isChecked}
inputValue={this.state.textItems[1].inputValue} handleInput={this.handleInput}
handleCheckbox={this.handleCheckbox}/>
<CheckboxTxtInput id={this.state.textItems[2].id} isChecked={this.state.textItems[2].isChecked}
inputValue={this.state.textItems[2].inputValue}
handleInput={this.handleInput} handleCheckbox={this.handleCheckbox}/>
<CheckboxTxtInput id={this.state.textItems[3].id} isChecked={this.state.textItems[3].isChecked}
inputValue={this.state.textItems[3].inputValue} handleInput={this.handleInput}
handleCheckbox={this.handleCheckbox}/>
</div>
<RenderText />
</div>
)
}
}
The simplest, React-like way to do this is to have a parent wrapper component - say LabeledCheckbox which contains your Text input and your Checkbox components.
When either of the child components do something, they call a callback provided by the parent, and the parent maintains the state for the two components, passing that state down into the props of both children.
The children in this case would never maintain their own state, instead simply calling callbacks and being prop-fed.
Create one component with checkbox and input field with the state of the checkbox and text field.
And then you can reuse it where you want.
You can do something like this :
class CheckboxTxtInput extends React.Component{
constructor(){
super();
this.state = {
checkbox: false,
inputValue: ""
}
}
handleCheckbox(e){
this.setState({checkbox: e.target.checked})
}
handleInput(e){
this.setState({inputValue: e.target.value})
}
render(){
return (
<div>
<input type="checkbox" onChange={this.handleCheckbox.bind(this)} checked={this.state.checkbox}/>
<input type="text" value={this.state.inputValue} disabled={this.state.checkbox} onChange={this.handleInput.bind(this)}/>
</div>
)
}
}
class Test extends React.Component {
render(){
return (
<div><CheckboxTxtInput /></div>
)
}
}
React.render(<Test />, document.getElementById('container'));
Here is the fiddle.
Hope this helps.

How to render a container dynamically in react-redux?

I am new to redux-react so excuse me if it is a stupid question.
I have a page which shows the list of some products in a table.
When I click on a product, I want to show details about that product in a panel overlaying the main page.
The problem is that the detail page has already a component and container class.
If I want to render the component I have to mix the main page and detail page containers together which I don't want. I want to keep each page component and container separate.
When I render container I get the error
Invariant Violation: Could not find "store" in either the context or props. Either wrap the root component in a , or explicitly pass "store" as a prop.
I don't know how to pass it and I googled about it I couldn't find a solution for my case. I don't want to initialize a new store.
Here is my click function to show the detail page.
onClick(){
ReactDOM.render(
<div>
<div className="my-panel" id="my-panel" data-toggler=".is-active">
<ProductDetailContainer />
<button className="button" data-toggle="my-panel">Close</button>
</div>
</div>,
wrapper
);
}
here is my product detail container code:
export class ProductDetailContainer extends RootContainer {
constructor(props) {
super(props);
this.state = {
productDetail: {}
};
}
componentDidMount() {
this.props.dispatch(someAction);
}
componentWillReceiveProps(nextProps) {
//some code here
}
handleRefresh() {
//some code here
}
render() {
return (
<div className="row small-12 columns">
<ProductDetailComponent
data={this.state.productDetail}
/>
</div>
);
}
}
ProductDetailContainer.propTypes = {
productDetail: PropTypes.object
};
export function mapStateToProps(state) {
return {
productDetail: state.productdetail
};
}
export default connect(mapStateToProps)(ProductDetailContainer);

Resources