I am having flow types error
Here is my error:
Cannot use field [1] as a computed property. Computed properties may only be primitive literal values, but this one may be either string literal amountFrom
Here is my code:
const handleChangeAmountInputs = (value, field) => {
if (value.includes(',')) {
setAmountFilter({
...amountFilter,
[field]: value.split(',').join(''),
});
} else {
setAmountFilter({...amountFilter, [field]: value});
}
};
and here is how i call the function
<TextInput
placeholder={'Enter amount'}
returnKeyType="done"
keyboardType="decimal-pad"
onChangeText={(value) => {
handleChangeAmountInputs(value, 'amountFrom');
}}
/>
Assuming based solely on the code snippet you've provided, flow must be implicitly typing your field arg as a string literal as opposed to a string.
If you add a type annotation to field, I think it should fix it.
const handleChangeAmountInputs = (value, field: string) => {
Please could someone help answer this:
I have 2 NumberInput controls, one input and the other is disabled. I need to input number in the first input field, the disabled field to show this number/100. The two NumberInput will have source fields that will save to the current record in the simpleform.
How do I do this in react-admin
Thanks
Easiest way is to use the method described in the docs under section Linking two inputs
In essence: You can create your own input component where you can access the form values via the hook useFormState. Then just assign the desired value transformed the way you want e.g. divided by 100.
Edit
Found one more even cleaner way - using the final-form-calculate to create a decorator and pass it to the <FormWithRedirect /> component like so:
import createDecorator from 'final-form-calculate'
const calculator = createDecorator(
// Calculations:
{
field: 'number1', // when the value of foo changes...
updates: {
number2: (fooValue, allValues) => allValues["number1"] * 2
}
})
...
<FormWithRedirect
...
decorators={[calculator]}
/>
Check out this code sandbox
Using FormDataConsumer
<FormDataConsumer>
{({ formData }) => (
<NumberInput defaultValue={formData.my_first_input / 100} source="second_input"/>
)}
</FormDataConsumer>
Using the useFormState hook
import { useFormState } from 'react-final-form';
...
const { values: { my_first_input }} = useFormState({ subscription: { values: true } });
...
<NumberInput defaultValue={my_first_input / 100} source="second_input"/>
Source: https://marmelab.com/react-admin/Inputs.html#linking-two-inputs
Dynamic
You need to use the useForm hook of react-final-form to make your input dynamic:
import { useForm, useFormState } from 'react-final-form';
...
const {change} = useForm();
const { values: { my_first_input }} = useFormState({ subscription: { values: true } });
useEffect(() => {
change('my_second_input', my_first_input / 100);
}, [change, my_first_input]);
...
<NumberInput defaultValue={my_first_input / 100} source="second_input"/>
I got a shorter solution to this question:
All I did was to do the calculation within FormDataConsumer. Now, I am able to get the calculated value and it updates the correct record in the array.
Thanks
<FormDataConsumer>
{({
formData, // The whole form data
scopedFormData, // The data for this item of the ArrayInput
getSource, // A function to get the valid source inside an ArrayInput
...rest
}) => {
if (typeof scopedFormData !== 'undefined') {
scopedFormData.total = scopedFormData.quantity * scopedFormData.unitprice;
return (
<NumberInput disabled defaultValue={scopedFormData.total} label="Total" source={getSource('total')} />
)
} else {
return(
<NumberInput disabled label="Total" source={getSource('total')} />
)
}
}}
I am new to React Native, and currently have two text input boxes and I would like it that when I change one the value shown in the other also changes. But then you will be allowed to edit the second text input and this will in tune change the first one. And so on...
I have tried setting the placeholder as the value, then that will change as the first text input changes, but it only works until you edit the text box.
Essentially I cannot figure out a way to have a text input and output on top of each other.
state = { valueOne: '', valueTwo: '' }
changeValueOne = (valueOne) => {
this.setState({
valueOne,
valueTwo: `${valueOne}-foo`
})
}
changeValueTwo = (valueTwo) => {
this.setState({
valueOne: `${valueTwo}-bar`,
valueTwo
})
}
render() {
const { valueOne, valueTwo } = this.state
return (
<View>
<Input onChangeText={this.changeValueOne} value={valueOne} />
<Input onChangeText={this.changeValueTwo} value={valueTwo} />
</View>
)
}
I am using ReactContext and Hooks to show and hide a Modal on click of a button.
Following is my Context code
const setPrivacyPolicyModalVisibility = dispatch => {
return ({visible}) => {
visible
? dispatch({type: 'enablePrivacyPolicyModalVisibility'})
: dispatch({type: 'disablePrivacyPolicyModalVisibility'});
};
};
And the reducer code for the same is something as follows
case 'enablePrivacyPolicyModalVisibility':
return {...state, ...{enablePrivacyPolicy: true}};
case 'disablePrivacyPolicyModalVisibility':
return {...state, ...{enablePrivacyPolicy: false}};
Some setup code in my class
const {state, setPrivacyPolicyModalVisibility} = useContext(Context);
const [privacyVisibility, setPrivacyVisibility] = useState(false);
on click of button I am calling the following code
<TouchableOpacity
onPress={() => {
setPrivacyVisibility(true);
console.log(`${privacyVisibility}`);
setPrivacyPolicyModalVisibility({privacyVisibility});
}}.....
As you can see I am console logging the privacyVisibility value but it is always false which I fail to understand
Following is my code in the component to hide or show the Modal
{state.enablePrivacyPolicy ? (
<SettingsPrivacyModal visible={true} />
) : (
<SettingsPrivacyModal visible={false} />
)}
The Modal code is proper as I have tried setting default value to true just to check if modal is visible then it works, but on click of button press the state value does not change and I am not able to see the modal as the value is always false
The issue seems to be in the onPress callback:
onPress={() => {
const privacyVisibility_new = !privacyVisibility;
console.log( privacyVisibility_new );
setPrivacyVisibility( privacyVisibility_new );
setPrivacyPolicyModalVisibility( privacyVisibility:privacyVisibility_new );
}}
When the cycle reaches the callback privacyVisibility has the default which is false. I think you are assuming that once setPrivacyVisibility is called, the privacyVisibility variable will have the new value in that same cycle; but it won't have the updated value until the component renders again.
setPrivacyPolicyModalVisibility doesn't seem to be correct. I am not sure where is dispatch exactly, but assuming it is at the same level as the function you can simply use it inside.
const setPrivacyPolicyModalVisibility = visible => {
if ( visible ) {
dispatch({ type: "enablePrivacyPolicyModalVisibility" });
} else {
dispatch({ type: "disablePrivacyPolicyModalVisibility" });
}
};
You might want to simplify your reducer and send directly the visible value:
const setPrivacyPolicyModalVisibility = visible =>
dispatch({ type: "setPrivacyPolicyModalVisibility", payload: visible });
.
case 'setPrivacyPolicyModalVisibility':
return { ...state, is_privacyPolicy_visible: action.payload };
Actually the error was simple. I am using the visible parameter as props in setPrivacyPolicyModalVisibility but while setting I am passing prop of different name
Thanks to #Alvaro for pointing me in the right direction
Attention: I have posted an answer down there, personally I think it's the best solution so far. Even though it's not the highest rated answer, but based on the result I'm getting, it is very efficient.
---------------------------------------------Original Question-------------------------------------------------------
Suppose I am writing a Twitter clone, but much simpler. I put each item in FlatList and render them.
To "like" a post, I press the "like" button on the post and the "like" button turns red, I press it again, it turns gray.
This is what I have so far: I store all the loaded posts in this.state, each post has a property called "liked", which is boolean, indicating whether this user has liked this post or not, when user presses "like", I go to state.posts and update the liked property of that post, and then use this.setState to update posts like so:
// 1. FlatList
<FlatList
...
data={this.state.posts}
renderItem={this.renderPost}
...
/>
// 2. renderPost
renderPost({ item, index }) {
return (
<View style={someStyle}>
... // display other properties of the post
// Then display the "like" button
<Icon
name='favorite'
size={25}
color={item.liked ? 'red' : 'gray'}
containerStyle={someStyle}
iconStyle={someStyle}
onPress={() => this.onLikePost({ item, index })}
/>
...
</View>
);
}
// 3. onLikePost
likePost({ item, index }) {
let { posts } = this.state;
let targetPost = posts[index];
// Flip the 'liked' property of the targetPost
targetPost.liked = !targetPost.liked;
// Then update targetPost in 'posts'
posts[index] = targetPost;
// Then reset the 'state.posts' property
this.setState({ posts });
}
This approach works, however, it is too slow. The color of the "like" button flips as I press it, but it usually takes about 1 second before the color changes. What I want is that the color would flip almost at the same time when I press it.
I do know why this would happen, I should probably not use this.setState, because when I do that, the posts state changed, and all posts get re-rendered, but what other approach can I try?
You can set extraData in FlatList:
<FlatList
...
extraData={this.state}
data={this.state.posts}
renderItem={this.renderPost}
...
/>
When state.posts or state.posts's item change, FlatList will re-render.
From FlatList#extradata:
A marker property for telling the list to re-render (since it implements PureComponent). If any of your renderItem, Header, Footer, etc. functions depend on anything outside of the data prop, stick it here and treat it immutably.
Update:
Functional component implementation:
export default function() {
// list of your data
const [list, setList] = React.useState([])
const [extraData, setExtraData] = React.useState(new Date())
// some update on the item of list[idx]
const someAction = (idx)=>{
list[idx].show = 1
setList(list)
setExtraData(new Date())
}
return (
<FlatList
// ...
data={list}
extraData={extraData}
/>
)
}
After updating list, I use setExtraData(new Date()) to tell the FlatList to re-render. Because the new time is different from the previous.
Don't get me wrong, #ShubhnikSingh's answer did help, but I retracted it because I found a better solution to this question, long time ago, and finally I remembered to post it here.
Suppose my post item contains these properties:
{
postId: "-L84e-aHwBedm1FHhcqv",
date: 1525566855,
message: "My Post",
uid: "52YgRFw4jWhYL5ulK11slBv7e583",
liked: false,
likeCount: 0,
commentCount: 0
}
Where liked represents whether the user viewing this post has liked this post, which will determine the color of the "like" button (by default, it's gray, but red if liked == true)
Here are the steps to recreate my solution: make "Post" a Component and render it in a FlatList. You can use React's PureComponent if you don't have any props that you pass to your Post such as an array or object that can be deceptively not shallow equal. If you don't know what that means, just use a regular Component and override shouldComponentUpdate as we do below.
class Post extends Component {
// This determines whether a rendered post should get updated
// Look at the states here, what could be changing as time goes by?
// Only 2 properties: "liked" and "likeCount", if the person seeing
// this post ever presses the "like" button
// This assumes that, unlike Twitter, updates do not come from other
// instances of the application in real time.
shouldComponentUpdate(nextProps, nextState) {
const { liked, likeCount } = nextProps
const { liked: oldLiked, likeCount: oldLikeCount } = this.props
// If "liked" or "likeCount" is different, then update
return liked !== oldLiked || likeCount !== oldLikeCount
}
render() {
return (
<View>
{/* ...render other properties */}
<TouchableOpacity
onPress={() => this.props.onPressLike(this.props.postId)}
>
<Icon name="heart" color={this.props.liked ? 'gray' : 'red'} />
</TouchableOpacity>
</View>
)
}
}
Then, create a PostList component that will be in charge of handling the logic for loading posts and handling like interactions:
class PostList extends Component {
/**
* As you can see, we are not storing "posts" as an array. Instead,
* we make it a JSON object. This allows us to access a post more concisely
* than if we stores posts as an array. For example:
*
* this.state.posts as an array
* findPost(postId) {
* return this.state.posts.find(post => post.id === postId)
* }
* findPost(postId) {
* return this.state.posts[postId]
* }
* a specific post by its "postId", you won't have to iterate
* through the whole array, you can just call "posts[postId]"
* to access it immediately:
* "posts": {
* "<post_id_1>": { "message": "", "uid": "", ... },
* "<post_id_2>": { "message": "", "uid": "", ... },
* "<post_id_3>": { "message": "", "uid": "", ... }
* }
* FlatList wants an array for its data property rather than an object,
* so we need to pass data={Object.values(this.state.posts)} rather than
* just data={this.state.posts} as one might expect.
*/
state = {
posts: {}
// Other states
}
renderItem = ({ item }) => {
const { date, message, uid, postId, other, props, here } = item
return (
<Post
date={date}
message={message}
uid={uid}
onPressLike={this.handleLikePost}
/>
)
}
handleLikePost = postId => {
let post = this.state.posts[postId]
const { liked, likeCount } = post
const newPost = {
...post,
liked: !liked,
likeCount: liked ? likeCount - 1 : likeCount + 1
}
this.setState({
posts: {
...this.state.posts,
[postId]: newPost
}
})
}
render() {
return (
<View style={{ flex: 1 }}>
<FlatList
data={Object.values(this.state.posts)}
renderItem={this.renderItem}
keyExtractor={({ item }) => item.postId}
/>
</View>
)
}
}
In summary:
1) Write a custom component (Post) for rendering each item in "FlatList"
2) Override the "shouldComponentUpdate" of the custom component (Post) function to tell the component when to update
Handle the "state of likes" in a parent component (PostList) and pass data down to each child
If you are testing on android than try turning off the developer mode. Or are you hitting some API and updating the post on the server and updating the like button in UI corresponding to the server response? If that is the case do tell me, I too have encountered this and I solved it. Also I have commented the second last line in your code which isn't needed.
// 1. FlatList
<FlatList
...
data={this.state.posts}
renderItem={this.renderPost}
...
/>
// 2. renderPost
renderPost({ item, index }) {
return (
<View style={someStyle}>
... // display other properties of the post
// Then display the "like" button
<Icon
name='favorite'
size={25}
color={item.liked ? 'red' : 'gray'}
containerStyle={someStyle}
iconStyle={someStyle}
onPress={() => this.onLikePost({ item, index })}
/>
...
</View>
);
}
// 3. onLikePost
likePost({ item, index }) {
let { posts } = this.state;
let targetPost = posts[index];
// Flip the 'liked' property of the targetPost
targetPost.liked = !targetPost.liked;
// Then update targetPost in 'posts'
// You probably don't need the following line.
// posts[index] = targetPost;
// Then reset the 'state.posts' property
this.setState({ posts });
}